Skip to content

Commit 4f5a21b

Browse files
committed
feat: add Docker support for vectorless-code
- Add Dockerfile with multi-layer caching strategy for efficient builds - Create entrypoint script with daemon supervisor for container lifecycle management - Add Docker Compose configuration for easy local development - Include comprehensive README with usage instructions and architecture details - Implement proper user permission handling for Linux environments - Configure volume mounts for workspace and runtime data persistence - Set up environment variables for configurable container behavior
1 parent d5a5960 commit 4f5a21b

4 files changed

Lines changed: 197 additions & 0 deletions

File tree

docker/Dockerfile

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Single-stage image with cache-friendly layer ordering.
2+
#
3+
# Stable layers (reuse across releases):
4+
# 1. apt install gosu + create vcc user
5+
# 2. install uv
6+
# 3. writable-path setup (mkdir /var/vectorless_code, chown to vcc)
7+
#
8+
# Per-release layers (invalidate when the source tree changes):
9+
# 4. COPY . /vcc-src — build context
10+
# 5. `uv pip install "vectorless-code"` — installs package + deps
11+
#
12+
# Uses glibc-based slim image — vectorless ships pre-built Rust wheels.
13+
FROM python:3.12-slim
14+
15+
RUN apt-get update \
16+
&& apt-get install -y --no-install-recommends gosu \
17+
&& rm -rf /var/lib/apt/lists/* \
18+
&& groupadd -g 1000 vcc \
19+
&& useradd -u 1000 -g 1000 -m vcc
20+
21+
RUN pip install --quiet uv
22+
23+
# Writable paths the daemon needs, pre-chowned to vcc.
24+
RUN mkdir -p /var/vectorless_code \
25+
&& chown -R vcc:vcc /var/vectorless_code
26+
27+
WORKDIR /workspace
28+
29+
# Runtime defaults — all overridable at `docker run -e ...` time.
30+
ENV VECTORLESS_CODE_DIR=/workspace/.vectorless_code \
31+
VECTORLESS_CODE_RUNTIME_DIR=/var/vectorless_code \
32+
VECTORLESS_CODE_DAEMON_SUPERVISED=1
33+
34+
COPY docker/entrypoint.sh /entrypoint.sh
35+
RUN chmod +x /entrypoint.sh
36+
ENTRYPOINT ["/entrypoint.sh"]
37+
38+
# ─── Per-release layer (last so only this one invalidates per release) ─────
39+
#
40+
# Default (PyPI flow): install vectorless-code from PyPI.
41+
# Release workflow / local tests override with:
42+
# --build-arg VCC_INSTALL_SPEC=/vcc-src
43+
# to install from the source tree.
44+
ARG VCC_INSTALL_SPEC=""
45+
RUN --mount=type=bind,source=.,target=/vcc-src,rw=true \
46+
if [ -z "$VCC_INSTALL_SPEC" ]; then \
47+
VCC_INSTALL_SPEC="vectorless-code"; \
48+
fi; \
49+
uv pip install --system "${VCC_INSTALL_SPEC}"

docker/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# vectorless-code Docker Usage
2+
3+
## Quick Start
4+
5+
```bash
6+
# Build the image
7+
docker build -t vcc:latest -f docker/Dockerfile .
8+
9+
# Run with Docker Compose (recommended)
10+
# macOS / Windows
11+
docker compose up -d
12+
13+
# Linux (aligns file ownership with your host user)
14+
PUID=$(id -u) PGID=$(id -g) docker compose up -d
15+
16+
# Then use the CLI normally
17+
docker compose exec vectorless-code vcc init
18+
docker compose exec vectorless-code vcc compile
19+
docker compose exec vectorless-code vcc ask "how does authentication work?"
20+
```
21+
22+
## Architecture
23+
24+
The container runs a **supervised daemon** that:
25+
- Keeps the container alive across daemon restarts
26+
- Auto-restarts when settings change
27+
- Gracefully handles `docker stop` via SIGTERM forwarding
28+
29+
## Environment Variables
30+
31+
| Variable | Description | Default |
32+
|----------|-------------|---------|
33+
| `VECTORLESS_HOST_PATH_MAPPING` | Map container paths to host paths | `/workspace=$HOME` |
34+
| `VECTORLESS_CODE_DIR` | Project config directory | `/workspace/.vectorless_code` |
35+
| `VECTORLESS_CODE_RUNTIME_DIR` | Daemon runtime directory | `/var/vectorless_code` |
36+
| `VECTORLESS_CODE_DAEMON_SUPERVISED` | Enable supervised mode | `1` |
37+
| `PUID` | Host user ID (Linux only) | - |
38+
| `PGID` | Host group ID (Linux only) | - |
39+
| `VCC_HOST_WORKSPACE` | Path to mount as workspace | `$HOME` |
40+
41+
## Volumes
42+
43+
- `/workspace` — Your codebase (bind mount)
44+
- `/var/vectorless_code` — Daemon runtime data (named volume)
45+
46+
## Daemon Management
47+
48+
```bash
49+
# Check daemon status
50+
docker compose exec vectorless-code vcc daemon status
51+
52+
# Restart daemon
53+
docker compose exec vectorless-code vcc daemon restart
54+
55+
# Stop container (stops daemon)
56+
docker compose down
57+
```
58+
59+
## Notes
60+
61+
- The daemon runs as the `vcc` user (UID 1000)
62+
- On Linux, use `PUID`/`PGID` to match your host user for file permissions
63+
- Cache is stored in `.vectorless_code/` within your workspace
64+
- The container auto-restarts the daemon on settings changes

docker/docker-compose.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# vectorless-code — Docker Compose quickstart.
2+
#
3+
# # macOS / Windows
4+
# docker compose up -d
5+
#
6+
# # Linux (aligns file ownership with your host user)
7+
# PUID=$(id -u) PGID=$(id -g) docker compose up -d
8+
#
9+
# By default your home directory is mounted into the container as the
10+
# workspace. Set VCC_HOST_WORKSPACE=/path/to/code to mount a narrower
11+
# directory instead.
12+
13+
services:
14+
vectorless-code:
15+
build:
16+
context: ..
17+
dockerfile: docker/Dockerfile
18+
image: vcc:latest
19+
container_name: vectorless-code
20+
volumes:
21+
- ${VCC_HOST_WORKSPACE:-${HOME}}:/workspace
22+
- vectorless-data:/var/vectorless_code
23+
environment:
24+
# Makes CLI and MCP output show your real paths
25+
# (e.g. `/Users/you/myproject/...`) instead of container paths
26+
# (e.g. `/workspace/myproject/...`).
27+
VECTORLESS_HOST_PATH_MAPPING: /workspace=${VCC_HOST_WORKSPACE:-${HOME}}
28+
# Linux only: set these so files written to your workspace are owned by
29+
# you rather than root. Not needed on macOS / Windows — leave empty.
30+
PUID: ${PUID:-}
31+
PGID: ${PGID:-}
32+
33+
volumes:
34+
vectorless-data:

docker/entrypoint.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/sh
2+
# Daemon supervisor entrypoint.
3+
#
4+
# Runs as PID 1 and keeps the container alive across daemon restarts. The
5+
# daemon intentionally exits whenever the client issues `StopRequest`
6+
# (settings-change mtime mismatch, version upgrade, etc.); the `while`
7+
# loop respawns it in place so the container stays up.
8+
#
9+
# Why not `exec vcc run-daemon`: if the daemon were PID 1, any daemon
10+
# exit would take the container down with it — breaking auto-restart on
11+
# settings edits.
12+
#
13+
# The SIGTERM/SIGINT trap forwards `docker stop` to the daemon child so
14+
# graceful shutdown still flows through the normal cleanup path.
15+
set -e
16+
17+
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
18+
groupmod -o -g "$PGID" vcc
19+
usermod -o -u "$PUID" vcc
20+
chown -R vcc:vcc /var/vectorless_code
21+
if [ -d /workspace/.vectorless_code ]; then
22+
chown vcc:vcc /workspace/.vectorless_code 2>/dev/null || true
23+
fi
24+
fi
25+
26+
run_daemon() {
27+
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
28+
gosu vcc vcc run-daemon
29+
else
30+
vcc run-daemon
31+
fi
32+
}
33+
34+
child=""
35+
trap 'if [ -n "$child" ]; then kill -TERM "$child" 2>/dev/null; wait "$child" 2>/dev/null; fi; exit 0' TERM INT
36+
37+
while true; do
38+
start_ts=$(date +%s)
39+
run_daemon &
40+
child=$!
41+
wait "$child" || true
42+
# Rate-limit respawns: sleep just long enough that successive starts are
43+
# >=1s apart. A clean settings-change exit with a long-running daemon
44+
# doesn't pay the 1s tax — only tight crash loops do.
45+
now=$(date +%s)
46+
delay=$((start_ts + 1 - now))
47+
if [ "$delay" -gt 0 ]; then
48+
sleep "$delay"
49+
fi
50+
done

0 commit comments

Comments
 (0)