diff --git a/.gitignore b/.gitignore index 648d894d..fe8a0ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,9 @@ instance/ # Scrapy stuff: .scrapy +# Setup sentinel +.setup-complete + # Sphinx documentation docs/_build/ diff --git a/Makefile b/Makefile index 6c3a63e3..8c4a43aa 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,10 @@ .PHONY: setup run deploy stop install uninstall build install-pre-commit -# Check if conda is available -ifeq (, $(shell which conda)) - $(error "Conda is not found in PATH. Please install Conda or add it to your PATH.") -endif +SETUP_SENTINEL := .setup-complete -# Setup - create .env file -setup: +setup: $(SETUP_SENTINEL) + +$(SETUP_SENTINEL): chmod +x setup.sh ./setup.sh @@ -16,7 +14,7 @@ run: conda run --no-capture-output -n hummingbot-api uvicorn main:app --reload # Deploy with Docker -deploy: +deploy: $(SETUP_SENTINEL) docker compose up -d # Stop all services @@ -25,16 +23,21 @@ stop: # Install conda environment install: + @if ! command -v conda >/dev/null 2>&1; then \ + echo "Error: Conda is not found in PATH. Please install Conda or add it to your PATH."; \ + exit 1; \ + fi @if conda env list | grep -q '^hummingbot-api '; then \ - echo "Environment already exists."; \ + echo "Environment already exists."; \ else \ - conda env create -f environment.yml; \ + conda env create -f environment.yml; \ fi $(MAKE) install-pre-commit $(MAKE) setup uninstall: conda env remove -n hummingbot-api -y + rm -f $(SETUP_SENTINEL) install-pre-commit: conda run -n hummingbot-api pip install pre-commit @@ -42,4 +45,4 @@ install-pre-commit: # Build Docker image build: - docker build -t hummingbot/hummingbot-api:latest . + docker build -t hummingbot/hummingbot-api:latest . \ No newline at end of file diff --git a/setup.sh b/setup.sh index 51cd2c44..296289e4 100755 --- a/setup.sh +++ b/setup.sh @@ -1,19 +1,219 @@ #!/bin/bash -# Hummingbot API Setup - Creates .env with sensible defaults +# Hummingbot API Setup - Creates .env with sensible defaults (Mac/Linux/WSL2) +# - On Linux (apt-based): installs build deps (gcc, build-essential) +# - Ensures Docker + Docker Compose are available (auto-installs on Linux via get.docker.com) -set -e +set -euo pipefail + +echo "Hummingbot API Setup" +echo "" + +has_cmd() { command -v "$1" >/dev/null 2>&1; } + +resolve_script_dir() { + local src="${BASH_SOURCE[0]}" + while [ -h "$src" ]; do + local dir + dir="$(cd -P "$(dirname "$src")" >/dev/null 2>&1 && pwd)" + src="$(readlink "$src")" + [[ "$src" != /* ]] && src="$dir/$src" + done + cd -P "$(dirname "$src")" >/dev/null 2>&1 && pwd +} + +SCRIPT_DIR="$(resolve_script_dir)" + +# Log file defaults to the script folder (fallback to /tmp if not writable) +LOG_FILE="${LOG_FILE:-${SCRIPT_DIR}/hummingbot-api-setup.log}" +if ! ( : >>"$LOG_FILE" ) 2>/dev/null; then + LOG_FILE="/tmp/hummingbot-api-setup.log" +fi + +run_quiet() { + # Usage: run_quiet + # Writes detailed output to LOG_FILE, but keeps terminal clean + "$@" >>"$LOG_FILE" 2>&1 +} + +# -------------------------- +# OS / Environment Detection +# -------------------------- +OS="$(uname -s || true)" +ARCH="$(uname -m || true)" + +is_linux() { [[ "${OS}" == "Linux" ]]; } +is_macos() { [[ "${OS}" == "Darwin" ]]; } + +docker_ok() { has_cmd docker; } + +docker_compose_ok() { + if has_cmd docker && docker compose version >/dev/null 2>&1; then + return 0 + fi + if has_cmd docker-compose && docker-compose version >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +need_sudo_or_die() { + if ! has_cmd sudo; then + echo "ERROR: 'sudo' is required for dependency installation on this system." + echo "Please install sudo (or run as root) and re-run this script." + exit 1 + fi +} + +# -------------------------- +# Linux Dependencies +# -------------------------- +install_linux_build_deps() { + if has_cmd apt-get; then + need_sudo_or_die + echo "[INFO] Detected Linux. Installing build dependencies (gcc, build-essential)... (logging to $LOG_FILE)" + + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get upgrade -y -qq + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq gcc build-essential + + echo "[OK] Build dependencies checked/installed." + else + echo "[WARN] Detected Linux, but 'apt-get' is not available. Skipping build dependency install." + fi +} + +ensure_curl_on_linux() { + if has_cmd curl; then + return 0 + fi + + if has_cmd apt-get; then + need_sudo_or_die + echo "[INFO] Installing curl (required for Docker install script)... (logging to $LOG_FILE)" + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq curl ca-certificates + echo "[OK] curl installed." + return 0 + fi + + echo "[WARN] curl is not installed and apt-get is unavailable. Please install curl and re-run." + return 1 +} + +# -------------------------- +# Docker Install / Validation +# -------------------------- +install_docker_linux() { + need_sudo_or_die + ensure_curl_on_linux + + echo "[INFO] Docker not found. Installing Docker using get.docker.com script... (logging to $LOG_FILE)" + run_quiet curl -fsSL https://get.docker.com -o get-docker.sh + run_quiet sudo sh get-docker.sh + run_quiet rm -f get-docker.sh + + if has_cmd systemctl; then + if systemctl is-system-running >/dev/null 2>&1; then + echo "[INFO] Enabling and starting Docker service..." + sudo systemctl enable docker >/dev/null 2>&1 || true + sudo systemctl start docker >/dev/null 2>&1 || true + fi + fi + + if has_cmd getent && getent group docker >/dev/null 2>&1; then + if [[ "${EUID}" -ne 0 ]]; then + echo "[INFO] Adding current user to 'docker' group (may require re-login)..." + sudo usermod -aG docker "$USER" >/dev/null 2>&1 || true + fi + fi +} + +ensure_docker_and_compose() { + if is_linux; then + if ! docker_ok; then + install_docker_linux + fi + + if ! docker_ok; then + echo "ERROR: Docker installation did not succeed or 'docker' is still not on PATH." + echo " Try opening a new shell and re-running, or verify Docker installation." + exit 1 + fi + + if ! docker_compose_ok; then + if has_cmd apt-get; then + need_sudo_or_die + echo "[INFO] Docker Compose not found. Attempting to install docker-compose-plugin... (logging to $LOG_FILE)" + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get update -qq + run_quiet sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-compose-plugin || true + fi + fi + + if ! docker_compose_ok; then + echo "ERROR: Docker Compose is not available." + echo " Expected either 'docker compose' (v2) or 'docker-compose' (v1)." + echo " On Ubuntu/Debian, try: sudo apt-get install -y docker-compose-plugin" + exit 1 + fi + elif is_macos; then + if ! docker_ok || ! docker_compose_ok; then + echo "ERROR: Docker and/or Docker Compose not found on macOS." + echo " Install Docker Desktop for Mac (Apple Silicon or Intel) and re-run this script." + echo " After installation, ensure 'docker' works in this terminal (you may need a new shell)." + exit 1 + fi + else + echo "[WARN] Unsupported/unknown OS '${OS}'. Proceeding without installing OS-level dependencies." + if ! docker_ok || ! docker_compose_ok; then + echo "ERROR: Docker and/or Docker Compose not found." + exit 1 + fi + fi + + echo "[OK] Docker detected: $(docker --version 2>/dev/null || true)" + if docker compose version >/dev/null 2>&1; then + echo "[OK] Docker Compose detected: $(docker compose version 2>/dev/null || true)" + else + echo "[OK] Docker Compose detected: $(docker-compose version 2>/dev/null || true)" + fi +} + +# -------------------------- +# Pre-flight (deps + docker) +# -------------------------- +echo "[INFO] OS=${OS} ARCH=${ARCH}" +if is_linux; then + install_linux_build_deps +fi + +ensure_docker_and_compose +echo "" + +# -------------------------- +# Existing .env creation flow +# -------------------------- +if [ -f ".env" ]; then + echo ".env file already exists. Skipping setup." + echo "" + exit 0 +fi + +# Clear screen before prompting user +if has_cmd clear; then + clear +else + printf "\033c" +fi echo "Hummingbot API Setup" echo "" -# Only prompt for password (most common customization) read -p "API password [default: admin]: " PASSWORD PASSWORD=${PASSWORD:-admin} read -p "Config password [default: admin]: " CONFIG_PASSWORD CONFIG_PASSWORD=${CONFIG_PASSWORD:-admin} -# Create .env with sensible defaults cat > .env << EOF # Hummingbot API Configuration USERNAME=admin @@ -38,10 +238,17 @@ GATEWAY_PASSPHRASE=admin BOTS_PATH=$(pwd) EOF +touch .setup-complete + echo "" echo ".env created successfully!" echo "" echo "Next steps:" -echo " make deploy # Start all services" -echo " make run # Run API locally (dev mode)" +echo "" +echo "Option 1: Start all services with Docker (recommended)" +echo " make deploy" +echo "" +echo "Option 2: Run API locally (dev mode)" +echo " make install # Creates the conda environment - Note: Please install the latest Anaconda version manually" +echo " make run # Run API" echo ""