mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-10 06:10:46 +00:00
4c60dd021e
* fix(ui): clarify container-scoped runtime warnings * docs(runtime): clarify docker image variant expectations * test(tools): align media path expectations with workspace policy * docs(tests): narrow message media path contract wording
255 lines
7.3 KiB
Bash
255 lines
7.3 KiB
Bash
#!/usr/bin/env bash
|
|
# GoClaw Docker setup — generates .env and docker-compose command for your chosen variant.
|
|
#
|
|
# Usage:
|
|
# ./scripts/setup-docker.sh # Interactive mode
|
|
# ./scripts/setup-docker.sh --variant full --with-ui
|
|
# ./scripts/setup-docker.sh --variant alpine --dev
|
|
#
|
|
# Variants:
|
|
# alpine — Base image (~50 MB), no runtimes
|
|
# node — Alpine + Node.js pre-installed
|
|
# python — Alpine + Python pre-installed
|
|
# full — Alpine + Node.js + Python + all skill dependencies
|
|
#
|
|
# Flags:
|
|
# --dev Mount local source for development (live reload)
|
|
# --with-ui Include web dashboard
|
|
# --port N Gateway port (default: 18790)
|
|
# --ui-port N Dashboard port (default: 3000)
|
|
# --pg-port N PostgreSQL port (default: 5432)
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Defaults ──
|
|
VARIANT=""
|
|
DEV_MODE=false
|
|
WITH_UI=false
|
|
PORT="${GOCLAW_PORT:-18790}"
|
|
UI_PORT="${GOCLAW_UI_PORT:-3000}"
|
|
PG_PORT="${POSTGRES_PORT:-5432}"
|
|
DATA_DIR="${GOCLAW_DATA_DIR:-./goclaw-data}"
|
|
|
|
# ── Parse args ──
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--variant) VARIANT="$2"; shift 2 ;;
|
|
--dev) DEV_MODE=true; shift ;;
|
|
--with-ui) WITH_UI=true; shift ;;
|
|
--port) PORT="$2"; shift 2 ;;
|
|
--ui-port) UI_PORT="$2"; shift 2 ;;
|
|
--pg-port) PG_PORT="$2"; shift 2 ;;
|
|
--data-dir) DATA_DIR="$2"; shift 2 ;;
|
|
--help|-h)
|
|
head -20 "$0" | grep '^#' | sed 's/^# \?//'
|
|
exit 0
|
|
;;
|
|
*) echo "Unknown option: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# ── Interactive variant selection ──
|
|
if [ -z "$VARIANT" ]; then
|
|
echo "Select GoClaw Docker variant:"
|
|
echo ""
|
|
echo " 1) alpine — Base image (~50 MB), no runtimes (published as :latest)"
|
|
echo " 2) node — + Node.js (published as :node)"
|
|
echo " 3) python — + Python (published as :python)"
|
|
echo " 4) full — + Node.js + Python + all skill deps (published as :full)"
|
|
echo ""
|
|
read -rp "Choice [1-4, default=1]: " choice
|
|
case "${choice:-1}" in
|
|
1|alpine) VARIANT="alpine" ;;
|
|
2|node) VARIANT="node" ;;
|
|
3|python) VARIANT="python" ;;
|
|
4|full) VARIANT="full" ;;
|
|
*) echo "Invalid choice"; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
# ── Resolve Docker image tag ──
|
|
DOCKER_IMAGE="digitop/goclaw"
|
|
case "$VARIANT" in
|
|
alpine) IMAGE_TAG="latest" ;;
|
|
node) IMAGE_TAG="node" ;;
|
|
python) IMAGE_TAG="python" ;;
|
|
full) IMAGE_TAG="full" ;;
|
|
*) echo "Unknown variant: $VARIANT"; exit 1 ;;
|
|
esac
|
|
|
|
echo ""
|
|
echo "Setting up GoClaw (${VARIANT})..."
|
|
|
|
# ── Create data directories ──
|
|
mkdir -p "${DATA_DIR}"/{config,data,workspace,skills,storage}
|
|
|
|
# ── Generate secrets if not present ──
|
|
ENV_FILE="${DATA_DIR}/.env"
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
GATEWAY_TOKEN="$(openssl rand -hex 32 2>/dev/null || head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n')"
|
|
ENCRYPTION_KEY="$(openssl rand -hex 16 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')"
|
|
PG_PASSWORD="$(openssl rand -hex 12 2>/dev/null || head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n')"
|
|
|
|
cat > "$ENV_FILE" <<EOF
|
|
# GoClaw environment — generated by setup-docker.sh
|
|
# Variant: ${VARIANT}
|
|
|
|
# Gateway
|
|
GOCLAW_GATEWAY_TOKEN=${GATEWAY_TOKEN}
|
|
GOCLAW_ENCRYPTION_KEY=${ENCRYPTION_KEY}
|
|
|
|
# Ports
|
|
GOCLAW_PORT=${PORT}
|
|
GOCLAW_UI_PORT=${UI_PORT}
|
|
POSTGRES_PORT=${PG_PORT}
|
|
|
|
# PostgreSQL
|
|
POSTGRES_USER=goclaw
|
|
POSTGRES_PASSWORD=${PG_PASSWORD}
|
|
POSTGRES_DB=goclaw
|
|
EOF
|
|
echo "Generated ${ENV_FILE} with random secrets."
|
|
else
|
|
echo "Using existing ${ENV_FILE}"
|
|
fi
|
|
|
|
# ── Generate docker-compose.yaml ──
|
|
COMPOSE_FILE="${DATA_DIR}/docker-compose.yaml"
|
|
|
|
cat > "$COMPOSE_FILE" <<YAML
|
|
# GoClaw Docker Compose — variant: ${VARIANT}
|
|
# Generated by setup-docker.sh. Edit as needed.
|
|
|
|
services:
|
|
postgres:
|
|
image: pgvector/pgvector:pg18
|
|
ports:
|
|
- "\${POSTGRES_PORT:-5432}:5432"
|
|
environment:
|
|
POSTGRES_USER: \${POSTGRES_USER:-goclaw}
|
|
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-goclaw}
|
|
POSTGRES_DB: \${POSTGRES_DB:-goclaw}
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-goclaw}"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 10
|
|
restart: unless-stopped
|
|
|
|
goclaw:
|
|
YAML
|
|
|
|
# Dev mode: build from source; Production: use pre-built image
|
|
if [ "$DEV_MODE" = true ]; then
|
|
cat >> "$COMPOSE_FILE" <<YAML
|
|
build:
|
|
context: $(pwd)
|
|
dockerfile: Dockerfile
|
|
args:
|
|
ENABLE_PYTHON: "$([ "$VARIANT" = "python" ] || [ "$VARIANT" = "full" ] && echo true || echo false)"
|
|
ENABLE_NODE: "$([ "$VARIANT" = "node" ] || [ "$VARIANT" = "full" ] && echo true || echo false)"
|
|
ENABLE_FULL_SKILLS: "$([ "$VARIANT" = "full" ] && echo true || echo false)"
|
|
YAML
|
|
else
|
|
cat >> "$COMPOSE_FILE" <<YAML
|
|
image: ${DOCKER_IMAGE}:${IMAGE_TAG}
|
|
YAML
|
|
fi
|
|
|
|
cat >> "$COMPOSE_FILE" <<YAML
|
|
ports:
|
|
- "\${GOCLAW_PORT:-18790}:18790"
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
- GOCLAW_HOST=0.0.0.0
|
|
- GOCLAW_PORT=18790
|
|
- GOCLAW_CONFIG=/app/data/config.json
|
|
- GOCLAW_SKILLS_DIR=/app/data/skills
|
|
- GOCLAW_WORKSPACE=/app/workspace
|
|
- GOCLAW_POSTGRES_DSN=postgres://\${POSTGRES_USER:-goclaw}:\${POSTGRES_PASSWORD:-goclaw}@postgres:5432/\${POSTGRES_DB:-goclaw}?sslmode=disable
|
|
volumes:
|
|
# Persistent data: config, agent files, runtime packages (pip/npm)
|
|
- goclaw-data:/app/data
|
|
# Workspace: agent working directory for file operations
|
|
- goclaw-workspace:/app/workspace
|
|
# Custom skills: user-installed skills persist across restarts
|
|
- goclaw-skills:/app/skills
|
|
# Storage: uploaded media, documents
|
|
- goclaw-storage:/app/storage
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
security_opt:
|
|
- no-new-privileges:true
|
|
cap_drop:
|
|
- ALL
|
|
read_only: true
|
|
tmpfs:
|
|
- /tmp:rw,noexec,nosuid,size=256m
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 1G
|
|
cpus: "2.0"
|
|
pids: 200
|
|
restart: unless-stopped
|
|
YAML
|
|
|
|
# Add web UI service if requested
|
|
if [ "$WITH_UI" = true ]; then
|
|
cat >> "$COMPOSE_FILE" <<YAML
|
|
|
|
goclaw-ui:
|
|
image: ${DOCKER_IMAGE}-web:latest
|
|
ports:
|
|
- "\${GOCLAW_UI_PORT:-3000}:80"
|
|
depends_on:
|
|
- goclaw
|
|
restart: unless-stopped
|
|
YAML
|
|
fi
|
|
|
|
cat >> "$COMPOSE_FILE" <<YAML
|
|
|
|
volumes:
|
|
postgres-data:
|
|
goclaw-data:
|
|
goclaw-workspace:
|
|
goclaw-skills:
|
|
goclaw-storage:
|
|
YAML
|
|
|
|
echo ""
|
|
echo "Generated ${COMPOSE_FILE}"
|
|
echo ""
|
|
echo "── Quick start ──"
|
|
echo ""
|
|
echo " cd ${DATA_DIR}"
|
|
echo " docker compose up -d"
|
|
echo ""
|
|
if [ "$WITH_UI" = true ]; then
|
|
echo " Gateway: http://localhost:${PORT}"
|
|
echo " Dashboard: http://localhost:${UI_PORT}"
|
|
else
|
|
echo " Gateway: http://localhost:${PORT}"
|
|
echo " Add --with-ui flag to include the web dashboard."
|
|
fi
|
|
echo ""
|
|
echo "── Volumes ──"
|
|
echo ""
|
|
echo " goclaw-data → /app/data Config, runtime packages (pip/npm cache)"
|
|
echo " goclaw-workspace → /app/workspace Agent file operations"
|
|
echo " goclaw-skills → /app/skills Custom installed skills"
|
|
echo " goclaw-storage → /app/storage Uploaded media & documents"
|
|
echo " postgres-data → PostgreSQL data"
|
|
echo ""
|
|
echo "── Useful commands ──"
|
|
echo ""
|
|
echo " docker compose logs -f goclaw # View logs"
|
|
echo " docker compose exec goclaw goclaw version # Check version"
|
|
echo " docker compose down # Stop"
|
|
echo " docker compose down -v # Stop + delete all data"
|