diff --git a/README.md b/README.md index c944855..92a7548 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,9 @@ cd zeroclaw # Optional: run onboarding in the same flow ./bootstrap.sh --onboard --api-key "sk-..." --provider openrouter [--model "openrouter/auto"] + +# Optional: run bootstrap + onboarding fully in Docker +./bootstrap.sh --docker ``` Remote one-liner (review first in security-sensitive environments): diff --git a/docs/one-click-bootstrap.md b/docs/one-click-bootstrap.md index d5ba762..25cd108 100644 --- a/docs/one-click-bootstrap.md +++ b/docs/one-click-bootstrap.md @@ -58,6 +58,15 @@ If you run Option B outside a repository checkout, the bootstrap script automati ## Optional onboarding modes +### Containerized onboarding (Docker) + +```bash +./bootstrap.sh --docker +``` + +This builds a local ZeroClaw image and launches onboarding inside a container while +persisting config/workspace to `./.zeroclaw-docker`. + ### Quick onboarding (non-interactive) ```bash diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 23e5c06..f256fa6 100755 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -25,6 +25,7 @@ Modes: Optional bootstrap mode can also install system dependencies and Rust. Options: + --docker Run bootstrap in Docker and launch onboarding inside the container --install-system-deps Install build dependencies (Linux/macOS) --install-rust Install Rust via rustup if missing --onboard Run onboarding after install @@ -38,6 +39,7 @@ Options: Examples: ./bootstrap.sh + ./bootstrap.sh --docker ./bootstrap.sh --install-system-deps --install-rust ./bootstrap.sh --onboard --api-key "sk-..." --provider openrouter [--model "openrouter/auto"] ./bootstrap.sh --interactive-onboard @@ -46,6 +48,8 @@ Examples: curl -fsSL https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/main/scripts/bootstrap.sh | bash Environment: + ZEROCLAW_DOCKER_DATA_DIR Host path for Docker config/workspace persistence + ZEROCLAW_DOCKER_IMAGE Docker image tag to build/run (default: zeroclaw-bootstrap:local) ZEROCLAW_API_KEY Used when --api-key is not provided ZEROCLAW_PROVIDER Used when --provider is not provided (default: openrouter) ZEROCLAW_MODEL Used when --model is not provided @@ -128,11 +132,93 @@ install_rust_toolchain() { fi } +ensure_docker_ready() { + if ! have_cmd docker; then + error "docker is not installed." + cat <<'MSG' >&2 +Install Docker first, then re-run with: + ./bootstrap.sh --docker +MSG + exit 1 + fi + + if ! docker info >/dev/null 2>&1; then + error "Docker daemon is not reachable." + error "Start Docker and re-run bootstrap." + exit 1 + fi +} + +run_docker_bootstrap() { + local docker_image docker_data_dir default_data_dir + docker_image="${ZEROCLAW_DOCKER_IMAGE:-zeroclaw-bootstrap:local}" + if [[ "$TEMP_CLONE" == true ]]; then + default_data_dir="$HOME/.zeroclaw-docker" + else + default_data_dir="$WORK_DIR/.zeroclaw-docker" + fi + docker_data_dir="${ZEROCLAW_DOCKER_DATA_DIR:-$default_data_dir}" + DOCKER_DATA_DIR="$docker_data_dir" + + mkdir -p "$docker_data_dir/.zeroclaw" "$docker_data_dir/workspace" + + if [[ "$SKIP_INSTALL" == true ]]; then + warn "--skip-install has no effect with --docker." + fi + + if [[ "$SKIP_BUILD" == false ]]; then + info "Building Docker image ($docker_image)" + docker build --target release -t "$docker_image" "$WORK_DIR" + else + info "Skipping Docker image build" + fi + + info "Docker data directory: $docker_data_dir" + + local onboard_cmd=() + if [[ "$INTERACTIVE_ONBOARD" == true ]]; then + info "Launching interactive onboarding in container" + onboard_cmd=(onboard --interactive) + else + if [[ -z "$API_KEY" ]]; then + cat <<'MSG' +==> Onboarding requested, but API key not provided. +Use either: + --api-key "sk-..." +or: + ZEROCLAW_API_KEY="sk-..." ./bootstrap.sh --docker +or run interactive: + ./bootstrap.sh --docker --interactive-onboard +MSG + exit 1 + fi + if [[ -n "$MODEL" ]]; then + info "Launching quick onboarding in container (provider: $PROVIDER, model: $MODEL)" + else + info "Launching quick onboarding in container (provider: $PROVIDER)" + fi + onboard_cmd=(onboard --api-key "$API_KEY" --provider "$PROVIDER") + if [[ -n "$MODEL" ]]; then + onboard_cmd+=(--model "$MODEL") + fi + fi + + docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + -e HOME=/zeroclaw-data \ + -e ZEROCLAW_WORKSPACE=/zeroclaw-data/workspace \ + -v "$docker_data_dir/.zeroclaw:/zeroclaw-data/.zeroclaw" \ + -v "$docker_data_dir/workspace:/zeroclaw-data/workspace" \ + "$docker_image" \ + "${onboard_cmd[@]}" +} + SCRIPT_PATH="${BASH_SOURCE[0]:-$0}" SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" >/dev/null 2>&1 && pwd || pwd)" ROOT_DIR="$(cd "$SCRIPT_DIR/.." >/dev/null 2>&1 && pwd || pwd)" REPO_URL="https://github.com/zeroclaw-labs/zeroclaw.git" +DOCKER_MODE=false INSTALL_SYSTEM_DEPS=false INSTALL_RUST=false RUN_ONBOARD=false @@ -145,6 +231,10 @@ MODEL="${ZEROCLAW_MODEL:-}" while [[ $# -gt 0 ]]; do case "$1" in + --docker) + DOCKER_MODE=true + shift + ;; --install-system-deps) INSTALL_SYSTEM_DEPS=true shift @@ -207,15 +297,24 @@ while [[ $# -gt 0 ]]; do esac done -if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then - install_system_deps +if [[ "$DOCKER_MODE" == true ]]; then + if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then + warn "--install-system-deps is ignored with --docker." + fi + if [[ "$INSTALL_RUST" == true ]]; then + warn "--install-rust is ignored with --docker." + fi +else + if [[ "$INSTALL_SYSTEM_DEPS" == true ]]; then + install_system_deps + fi + + if [[ "$INSTALL_RUST" == true ]]; then + install_rust_toolchain + fi fi -if [[ "$INSTALL_RUST" == true ]]; then - install_rust_toolchain -fi - -if ! have_cmd cargo; then +if [[ "$DOCKER_MODE" == false ]] && ! have_cmd cargo; then error "cargo is not installed." cat <<'MSG' >&2 Install Rust first: https://rustup.rs/ @@ -265,6 +364,31 @@ echo " workspace: $WORK_DIR" cd "$WORK_DIR" +if [[ "$DOCKER_MODE" == true ]]; then + ensure_docker_ready + if [[ "$RUN_ONBOARD" == false ]]; then + RUN_ONBOARD=true + if [[ -z "$API_KEY" ]]; then + INTERACTIVE_ONBOARD=true + fi + fi + run_docker_bootstrap + cat <<'DONE' + +✅ Docker bootstrap complete. + +Your containerized ZeroClaw data is persisted under: +DONE + echo " $DOCKER_DATA_DIR" + cat <<'DONE' + +Next steps: + ./bootstrap.sh --docker --interactive-onboard + ./bootstrap.sh --docker --api-key "sk-..." --provider openrouter +DONE + exit 0 +fi + if [[ "$SKIP_BUILD" == false ]]; then info "Building release binary" cargo build --release --locked