Files
Jiangzhou 92d611968e feat: unified Docker workspace mount with supervised daemon (#135)
* feat: unified Docker workspace mount with supervised daemon

Reshape the Docker experience around a single bind mount and a single
named volume. Global settings live on the host under
$HOME/.cocoindex_code/ (visible and editable); index data and the model
cache persist in one cocoindex-data volume; daemon runtime state stays
on the container's native filesystem.

CLI and MCP output now show host-side paths via a bidirectional
COCOINDEX_CODE_HOST_PATH_MAPPING translator. A shell wrapper that
forwards $PWD (COCOINDEX_CODE_HOST_CWD) lets ccc work from any project
subdirectory on the host.

The daemon tolerates a missing global_settings.yml (starts in
no-settings mode) so ccc init's interactive picker works in Docker on
first run. A supervisor restart loop in the entrypoint, driven by a new
COCOINDEX_CODE_DAEMON_SUPERVISED contract, makes settings-change
auto-restart safe — editing global_settings.yml triggers an in-place
daemon respawn without taking the container down.

Linux ownership alignment via PUID/PGID, gosu privilege drop, and a
coco user baked into the image. Release workflow now publishes to both
Docker Hub (cocoindex/cocoindex-code) and GHCR
(ghcr.io/cocoindex-io/cocoindex-code).

Also:
- Merge cocoindex-db and cocoindex-model-cache into a single volume
- find_parent_with_marker requires .cocoindex_code/settings.yml, so a
  workspace-root global-only dir doesn't trigger nested-init warnings
- New pytest marker `docker_e2e` gates the Docker-backed E2E suite
  (excluded from default pytest runs)

* fix: mypy on Windows for POSIX-only os.getuid/getgid calls
2026-04-14 13:30:50 -07:00

51 lines
1.6 KiB
Bash

#!/bin/sh
# Daemon supervisor entrypoint.
#
# Runs as PID 1 and keeps the container alive across daemon restarts. The
# daemon intentionally exits whenever the client issues `StopRequest`
# (settings-change mtime mismatch, version upgrade, etc.); the `while`
# loop respawns it in place so the container stays up.
#
# Why not `exec ccc run-daemon`: if the daemon were PID 1, any daemon
# exit would take the container down with it — breaking auto-restart on
# `global_settings.yml` edits.
#
# The SIGTERM/SIGINT trap forwards `docker stop` to the daemon child so
# graceful shutdown still flows through the normal cleanup path.
set -e
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
groupmod -o -g "$PGID" coco
usermod -o -u "$PUID" coco
chown -R coco:coco /var/cocoindex /var/run/cocoindex_code
if [ -d /workspace/.cocoindex_code ]; then
chown coco:coco /workspace/.cocoindex_code 2>/dev/null || true
fi
fi
run_daemon() {
if [ -n "$PUID" ] && [ -n "$PGID" ]; then
gosu coco ccc run-daemon
else
ccc run-daemon
fi
}
child=""
trap 'if [ -n "$child" ]; then kill -TERM "$child" 2>/dev/null; wait "$child" 2>/dev/null; fi; exit 0' TERM INT
while true; do
start_ts=$(date +%s)
run_daemon &
child=$!
wait "$child" || true
# Rate-limit respawns: sleep just long enough that successive starts are
# >=1s apart. A clean settings-change exit with a long-running daemon
# doesn't pay the 1s tax — only tight crash loops do.
now=$(date +%s)
delay=$((start_ts + 1 - now))
if [ "$delay" -gt 0 ]; then
sleep "$delay"
fi
done