mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-11 03:13:24 +00:00
ci: publish Docker images to GHCR and Docker Hub (#237)
* feat(ci): add node/python/full runtime variants to Docker publish Add runtime image variants alongside existing build-tag variants: - :node (pre-installed Node.js) - :python (pre-installed Python) - :full (Node.js + Python + all skill deps) * feat(ci): add install scripts, release binaries, and Discord notifications - scripts/install.sh: one-liner binary installer from GitHub Releases - scripts/setup-docker.sh: interactive Docker setup with variant selection (alpine/node/python/full), auto-generates .env + docker-compose.yaml with persistent volumes for data, skills, workspace, storage - release.yaml: build cross-platform binaries (linux/darwin × amd64/arm64) and attach to GitHub Release, notify Discord on new releases - .gitignore: ignore Apple Double (._*) files from external volumes * docs(docker): add pre-built image references and update docker-compose workflows - Add `image:` directives to docker-compose.yml, docker-compose.selfservice.yml, and docker-compose.upgrade.yml pointing to ghcr.io/nextlevelbuilder/goclaw pre-built images - Add Docker Hub mirror references (digitop/goclaw) for public access - Document available image tags (latest, node, python, full, otel, tsnet, redis) with descriptions - Update README with pre-built image pull instructions and semver tag examples - Clarify deployment workflows: use pre-built images by default (no --build), add --build only when building from source - Update upgrade workflow to pull pre-built images instead of rebuild - Add note about build args requirement for otel/tsnet/redis overlays - Update .dockerignore to exclude macOS temp files (._*) * fix(ci): use claude_code_oauth_token instead of anthropic_api_key
This commit is contained in:
12 files changed
+540
-34
No files matched your search
@@ -1,5 +1,6 @@
|
||||
.git
|
||||
.github
|
||||
._*
|
||||
.env*
|
||||
.dockerignore
|
||||
*.md
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
id: claude-review
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
||||
plugins: 'code-review@claude-code-plugins'
|
||||
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
# This is an optional setting that allows Claude to read CI results on PRs
|
||||
additional_permissions: |
|
||||
|
||||
@@ -19,31 +19,64 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# ── Runtime variants ──
|
||||
- variant: latest
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
suffix: ""
|
||||
- variant: otel
|
||||
enable_otel: "true"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
suffix: "-otel"
|
||||
- variant: tsnet
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "true"
|
||||
enable_redis: "false"
|
||||
suffix: "-tsnet"
|
||||
- variant: redis
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "true"
|
||||
suffix: "-redis"
|
||||
enable_redis: "false"
|
||||
enable_node: "false"
|
||||
enable_python: "false"
|
||||
enable_full_skills: "false"
|
||||
- variant: node
|
||||
suffix: "-node"
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
enable_node: "true"
|
||||
enable_python: "false"
|
||||
enable_full_skills: "false"
|
||||
- variant: python
|
||||
suffix: "-python"
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
enable_node: "false"
|
||||
enable_python: "true"
|
||||
enable_full_skills: "false"
|
||||
- variant: full
|
||||
enable_otel: "true"
|
||||
enable_tsnet: "true"
|
||||
enable_redis: "true"
|
||||
suffix: "-full"
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
enable_node: "true"
|
||||
enable_python: "true"
|
||||
enable_full_skills: "true"
|
||||
# ── Build-tag variants ──
|
||||
- variant: otel
|
||||
suffix: "-otel"
|
||||
enable_otel: "true"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "false"
|
||||
enable_node: "false"
|
||||
enable_python: "false"
|
||||
enable_full_skills: "false"
|
||||
- variant: tsnet
|
||||
suffix: "-tsnet"
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "true"
|
||||
enable_redis: "false"
|
||||
enable_node: "false"
|
||||
enable_python: "false"
|
||||
enable_full_skills: "false"
|
||||
- variant: redis
|
||||
suffix: "-redis"
|
||||
enable_otel: "false"
|
||||
enable_tsnet: "false"
|
||||
enable_redis: "true"
|
||||
enable_node: "false"
|
||||
enable_python: "false"
|
||||
enable_full_skills: "false"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -93,6 +126,9 @@ jobs:
|
||||
ENABLE_OTEL=${{ matrix.enable_otel }}
|
||||
ENABLE_TSNET=${{ matrix.enable_tsnet }}
|
||||
ENABLE_REDIS=${{ matrix.enable_redis }}
|
||||
ENABLE_NODE=${{ matrix.enable_node }}
|
||||
ENABLE_PYTHON=${{ matrix.enable_python }}
|
||||
ENABLE_FULL_SKILLS=${{ matrix.enable_full_skills }}
|
||||
VERSION=${{ github.ref_name }}
|
||||
cache-from: type=gha,scope=${{ matrix.variant }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.variant }}
|
||||
|
||||
@@ -10,12 +10,86 @@ permissions:
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.semrel.outputs.version }}
|
||||
released: ${{ steps.semrel.outputs.version != '' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: go-semantic-release/action@v1
|
||||
- id: semrel
|
||||
uses: go-semantic-release/action@v1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allow-initial-development-versions: true
|
||||
|
||||
# Build cross-platform binaries and attach to release
|
||||
build-binaries:
|
||||
needs: release
|
||||
if: needs.release.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
- goos: darwin
|
||||
goarch: arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- name: Build binary
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
VERSION: v${{ needs.release.outputs.version }}
|
||||
run: |
|
||||
CGO_ENABLED=0 go build \
|
||||
-ldflags="-s -w -X github.com/nextlevelbuilder/goclaw/cmd.Version=${VERSION}" \
|
||||
-o goclaw .
|
||||
tar -czf "goclaw-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" goclaw
|
||||
|
||||
- name: Upload to release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release upload "v${{ needs.release.outputs.version }}" \
|
||||
"goclaw-${{ needs.release.outputs.version }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" \
|
||||
--clobber
|
||||
|
||||
# Notify Discord on new release
|
||||
notify-discord:
|
||||
needs: [release, build-binaries]
|
||||
if: needs.release.outputs.released == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Discord notification
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
VERSION: v${{ needs.release.outputs.version }}
|
||||
run: |
|
||||
curl -fsSL -H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"embeds\": [{
|
||||
\"title\": \"GoClaw ${VERSION} Released\",
|
||||
\"url\": \"https://github.com/${{ github.repository }}/releases/tag/${VERSION}\",
|
||||
\"color\": 5814783,
|
||||
\"fields\": [
|
||||
{\"name\": \"Docker\", \"value\": \"\`docker pull digitop/goclaw:latest\`\", \"inline\": false},
|
||||
{\"name\": \"Install\", \"value\": \"\`curl -fsSL https://raw.githubusercontent.com/${{ github.repository }}/main/scripts/install.sh | bash\`\", \"inline\": false}
|
||||
],
|
||||
\"footer\": {\"text\": \"${{ github.repository }}\"},
|
||||
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
|
||||
}]
|
||||
}" \
|
||||
"$DISCORD_WEBHOOK_URL"
|
||||
+3
-1
@@ -10,6 +10,7 @@ openclaw-go
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
._*
|
||||
|
||||
# Environment
|
||||
.env*
|
||||
@@ -36,6 +37,7 @@ tmp
|
||||
deploy-*.sh
|
||||
|
||||
docs/technical-diaries
|
||||
docs/zuey
|
||||
plans
|
||||
release-manifest.json
|
||||
k8s-*/*
|
||||
@@ -43,4 +45,4 @@ k8s-*/*
|
||||
# Test credentials (never commit)
|
||||
tests/**/creds.json
|
||||
ui/simple-saas
|
||||
*.tar
|
||||
*.tar
|
||||
@@ -344,10 +344,14 @@ The script creates `.env` from `.env.example`, auto-generates `GOCLAW_ENCRYPTION
|
||||
|
||||
```bash
|
||||
# Recommended: Gateway + Web Dashboard (http://localhost:3000)
|
||||
# Pull pre-built images:
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.selfservice.yml up -d
|
||||
|
||||
# Or build from source:
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.selfservice.yml up -d --build
|
||||
|
||||
# Without dashboard
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d --build
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
|
||||
|
||||
# + OpenTelemetry tracing (Jaeger at http://localhost:16686)
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.otel.yml up -d --build
|
||||
@@ -440,7 +444,40 @@ CGO_ENABLED=0 go build -ldflags="-s -w" -tags "otel,tsnet" -o goclaw .
|
||||
|
||||
> Optional features are gated behind build tags to avoid binary bloat. OTel adds ~11 MB (gRPC + protobuf). Tailscale adds ~20 MB (tsnet + WireGuard). The base build includes in-app tracing backed by PostgreSQL and localhost-only access.
|
||||
|
||||
### Docker Build
|
||||
### Docker Images (Pre-built)
|
||||
|
||||
Pre-built multi-arch images (linux/amd64 + linux/arm64) are published to **GHCR** and **Docker Hub** on every release:
|
||||
|
||||
```bash
|
||||
# GHCR (recommended)
|
||||
docker pull ghcr.io/nextlevelbuilder/goclaw:latest
|
||||
|
||||
# Docker Hub
|
||||
docker pull digitop/goclaw:latest
|
||||
```
|
||||
|
||||
**Available tags:**
|
||||
|
||||
| Tag | Description |
|
||||
| ---------- | -------------------------------------- |
|
||||
| `latest` | Base image (~50 MB Alpine) |
|
||||
| `node` | + Node.js runtime for JS tools |
|
||||
| `python` | + Python runtime for Python tools |
|
||||
| `full` | + Node.js + Python + all bundled skills |
|
||||
| `otel` | + OpenTelemetry tracing support |
|
||||
| `tsnet` | + Tailscale VPN mesh listener |
|
||||
| `redis` | + Redis cache backend |
|
||||
|
||||
Semver tags are also available: `1.0.0`, `1.0`, etc. (e.g. `ghcr.io/nextlevelbuilder/goclaw:1.0.0-python`).
|
||||
|
||||
**Web Dashboard:**
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/nextlevelbuilder/goclaw-web:latest
|
||||
docker pull digitop/goclaw-web:latest
|
||||
```
|
||||
|
||||
### Docker Build (from source)
|
||||
|
||||
```bash
|
||||
# Standard image (~50MB Alpine)
|
||||
@@ -682,13 +719,13 @@ Composable files for different deployment scenarios:
|
||||
# Prepare .env (auto-generates secrets, prompts for API key)
|
||||
chmod +x prepare-env.sh && ./prepare-env.sh
|
||||
|
||||
# Managed (PostgreSQL)
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d --build
|
||||
# Using pre-built images (no --build flag):
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
|
||||
|
||||
# Managed + Web Dashboard (http://localhost:3000)
|
||||
docker compose -f docker-compose.yml \
|
||||
-f docker-compose.postgres.yml \
|
||||
-f docker-compose.selfservice.yml up -d --build
|
||||
-f docker-compose.selfservice.yml up -d
|
||||
|
||||
# Managed + Web Dashboard + OpenTelemetry (Jaeger UI at http://localhost:16686)
|
||||
docker compose -f docker-compose.yml \
|
||||
@@ -705,15 +742,21 @@ docker compose -f docker-compose.yml \
|
||||
curl http://localhost:18790/health
|
||||
```
|
||||
|
||||
> **Note:** Omit `--build` to use pre-built images from GHCR. Add `--build` to build from source. Overlays that require build args (otel, tsnet, redis, sandbox) need `--build`.
|
||||
|
||||
### Upgrading (Docker Compose)
|
||||
|
||||
**Simple upgrade** — pull the latest code, rebuild, and restart. The entrypoint automatically runs `goclaw upgrade` (schema migrations + data hooks) before starting:
|
||||
**Simple upgrade** — pull the latest images and restart. The entrypoint automatically runs `goclaw upgrade` (schema migrations + data hooks) before starting:
|
||||
|
||||
```bash
|
||||
# Pull latest code
|
||||
git pull
|
||||
# Using pre-built images (recommended):
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml \
|
||||
-f docker-compose.selfservice.yml pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml \
|
||||
-f docker-compose.selfservice.yml up -d
|
||||
|
||||
# Rebuild and restart (auto-upgrades database on start)
|
||||
# Or build from source:
|
||||
git pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.postgres.yml \
|
||||
-f docker-compose.selfservice.yml up -d --build
|
||||
```
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
services:
|
||||
goclaw-ui:
|
||||
image: ghcr.io/nextlevelbuilder/goclaw-web:latest
|
||||
build:
|
||||
context: ./ui/web
|
||||
dockerfile: Dockerfile
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
services:
|
||||
upgrade:
|
||||
image: ghcr.io/nextlevelbuilder/goclaw:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
||||
+8
-2
@@ -1,14 +1,20 @@
|
||||
# Base docker-compose — shared service definition.
|
||||
# Pre-built images: ghcr.io/nextlevelbuilder/goclaw (also on Docker Hub: digitop/goclaw)
|
||||
#
|
||||
# Combine with overlays for your deployment:
|
||||
#
|
||||
# Recommended (+ Web Dashboard):
|
||||
# Recommended (+ Web Dashboard, pre-built images):
|
||||
# docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.selfservice.yml up -d
|
||||
#
|
||||
# Build from source (add --build):
|
||||
# docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.selfservice.yml up -d --build
|
||||
#
|
||||
# Without dashboard: docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d --build
|
||||
# Without dashboard: docker compose -f docker-compose.yml -f docker-compose.postgres.yml up -d
|
||||
# With OTel tracing: docker compose -f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.otel.yml up -d --build
|
||||
|
||||
services:
|
||||
goclaw:
|
||||
image: ghcr.io/nextlevelbuilder/goclaw:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# GoClaw installer — downloads the latest binary from GitHub Releases.
|
||||
#
|
||||
# Usage:
|
||||
# curl -fsSL https://raw.githubusercontent.com/nextlevelbuilder/goclaw/main/scripts/install.sh | bash
|
||||
# curl -fsSL ... | bash -s -- --version v1.30.0
|
||||
# curl -fsSL ... | bash -s -- --dir /opt/goclaw
|
||||
#
|
||||
# Supported: Linux (amd64/arm64), macOS (amd64/arm64)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO="nextlevelbuilder/goclaw"
|
||||
INSTALL_DIR="${GOCLAW_INSTALL_DIR:-/usr/local/bin}"
|
||||
VERSION=""
|
||||
|
||||
# ── Parse args ──
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--version) VERSION="$2"; shift 2 ;;
|
||||
--dir) INSTALL_DIR="$2"; shift 2 ;;
|
||||
--help|-h)
|
||||
echo "Usage: install.sh [--version v1.x.x] [--dir /path]"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Detect OS/arch ──
|
||||
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
ARCH="$(uname -m)"
|
||||
case "$ARCH" in
|
||||
x86_64) ARCH="amd64" ;;
|
||||
aarch64|arm64) ARCH="arm64" ;;
|
||||
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
|
||||
case "$OS" in
|
||||
linux|darwin) ;;
|
||||
*) echo "Unsupported OS: $OS"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# ── Resolve version ──
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Fetching latest release..."
|
||||
VERSION="$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')"
|
||||
fi
|
||||
echo "Installing GoClaw ${VERSION} (${OS}/${ARCH})..."
|
||||
|
||||
# ── Download ──
|
||||
ASSET="goclaw-${VERSION#v}-${OS}-${ARCH}.tar.gz"
|
||||
URL="https://github.com/${REPO}/releases/download/${VERSION}/${ASSET}"
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
echo "Downloading ${URL}..."
|
||||
curl -fsSL -o "${TMP}/${ASSET}" "$URL"
|
||||
|
||||
# ── Extract & install ──
|
||||
tar -xzf "${TMP}/${ASSET}" -C "$TMP"
|
||||
|
||||
# Check write permission, use sudo if needed
|
||||
if [ -w "$INSTALL_DIR" ]; then
|
||||
cp "${TMP}/goclaw" "${INSTALL_DIR}/goclaw"
|
||||
chmod +x "${INSTALL_DIR}/goclaw"
|
||||
else
|
||||
echo "Installing to ${INSTALL_DIR} (requires sudo)..."
|
||||
sudo cp "${TMP}/goclaw" "${INSTALL_DIR}/goclaw"
|
||||
sudo chmod +x "${INSTALL_DIR}/goclaw"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "GoClaw ${VERSION} installed to ${INSTALL_DIR}/goclaw"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Set up PostgreSQL (pgvector):"
|
||||
echo " docker run -d --name goclaw-pg -p 5432:5432 -e POSTGRES_PASSWORD=goclaw pgvector/pgvector:pg18"
|
||||
echo ""
|
||||
echo " 2. Run database migrations:"
|
||||
echo " export GOCLAW_POSTGRES_DSN='postgres://postgres:goclaw@localhost:5432/postgres?sslmode=disable'"
|
||||
echo " goclaw migrate up"
|
||||
echo ""
|
||||
echo " 3. Start the onboard wizard:"
|
||||
echo " goclaw onboard"
|
||||
echo ""
|
||||
echo " 4. Start the gateway:"
|
||||
echo " goclaw"
|
||||
@@ -0,0 +1,254 @@
|
||||
#!/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"
|
||||
echo " 2) node — + Node.js (for JS/TS skills)"
|
||||
echo " 3) python — + Python (for Python skills)"
|
||||
echo " 4) full — + Node.js + Python + all skill deps"
|
||||
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"
|
||||
Reference in new issue
Block a user