From d06bbe88fa57a83c675474f47b83ff209862093c Mon Sep 17 00:00:00 2001 From: Michael Feng Date: Mon, 1 Jun 2026 16:12:41 -0700 Subject: [PATCH 1/4] Add install.sh: unified Hummingbot/Condor install entry point One-command installer (curl | bash) that the website's Hummingbot/Condor tabs point at. It routes the Condor choice to the existing, unchanged setup.sh, and stubs the Hummingbot client path for P2. - Interactive Hummingbot-or-Condor wizard (falls back to flags in non-TTY). - Condor: hands off to setup.sh (sibling checkout or fetched from main), feeding it /dev/tty so its prompts work under curl | bash. - --doctor health check (docker, compose, uv, tmux, API :8000, condor tmux). - curl|bash-safe idioms: main "$@" as the final line, set -euo pipefail, platform/musl detection, no sudo. Co-Authored-By: Claude Opus 4.8 (1M context) --- install.sh | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100755 install.sh diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..8eee9d1fe --- /dev/null +++ b/install.sh @@ -0,0 +1,218 @@ +#!/usr/bin/env bash +# +# Hummingbot installer — one entry point to install Hummingbot or Condor. +# +# curl -fsSL https://hummingbot.org/install.sh | bash # interactive wizard +# curl -fsSL https://hummingbot.org/install.sh | bash -s -- --condor # Condor (existing process) +# curl -fsSL https://hummingbot.org/install.sh | bash -s -- --doctor # health check only +# +# The user picks Hummingbot or Condor (in the website UI, or this wizard). +# • Condor → routed to the EXISTING, unchanged deploy/setup.sh. We don't reimplement it. +# • Hummingbot → the client install + `hummingbot` wrapper + LLM plugin (built out in P2). +# +# See apps/docs/INSTALL_WIZARD_PLAN.md in hummingbot/hummingbot-web. + +main() { + set -euo pipefail + + REPO_RAW="${DEPLOY_RAW_BASE:-https://raw.githubusercontent.com/hummingbot/deploy/main}" + BASE_DIR="${HUMMINGBOT_INSTALL:-$HOME}" + API_DIR="$BASE_DIR/hummingbot-api" + CONDOR_DIR="$BASE_DIR/condor" + STATE_DIR="$HOME/.hummingbot" + + PRODUCT="" # condor | hummingbot (empty -> ask) + MODE="install" # install | upgrade | doctor + + ui_init + parse_args "$@" + + [[ "$MODE" == "doctor" ]] && { run_doctor; exit $?; } + + banner + detect_platform + require_cmd curl + + [[ -z "$PRODUCT" ]] && PRODUCT="$(choose_product)" + case "$PRODUCT" in + condor) run_condor ;; # exec's the existing installer — never returns + hummingbot) install_hummingbot ;; + *) error "Unknown product: $PRODUCT" ;; + esac +} + +# ── UI helpers ──────────────────────────────────────────────────────────────── +ui_init() { + if [[ -t 1 ]]; then + BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[0;31m'; GRN=$'\033[0;32m' + YEL=$'\033[1;33m'; CYN=$'\033[0;36m'; NC=$'\033[0m' + else + BOLD=""; DIM=""; RED=""; GRN=""; YEL=""; CYN=""; NC="" + fi +} +info() { printf '%s\n' "${CYN}→${NC} $*"; } +warn() { printf '%s\n' "${YEL}!${NC} $*" >&2; } +success() { printf '%s\n' "${GRN}✓${NC} $*"; } +error() { printf '%s\n' "${RED}✗ $*${NC}" >&2; exit 1; } +step() { printf '\n%s\n' "${BOLD}$*${NC}"; } +tildify() { printf '%s' "${1/#$HOME/~}"; } # replacement '~' is literal; no tilde expansion here + +banner() { + printf '\n%s\n%s\n\n' "${BOLD}🐦 Hummingbot installer${NC}" "${DIM}open-source framework for agentic trading${NC}" +} + +# Under `curl | bash`, stdin is the script — interactive input must come from /dev/tty. +is_promptable() { [[ -t 1 && -r /dev/tty ]]; } + +# prompt VAR "Question" "default" +prompt() { + local __var="$1" __q="$2" __def="${3:-}" __ans="" + if ! is_promptable; then + [[ -n "$__def" ]] && { printf -v "$__var" '%s' "$__def"; return 0; } + error "Need a value for '$__q' but no terminal is available." + fi + if [[ -n "$__def" ]]; then printf '%s ' "${CYN}?${NC} $__q ${DIM}[$__def]${NC}:" > /dev/tty + else printf '%s ' "${CYN}?${NC} $__q:" > /dev/tty; fi + read -r __ans < /dev/tty || true + printf -v "$__var" '%s' "${__ans:-$__def}" +} + +# ── args / platform ─────────────────────────────────────────────────────────── +usage() { + cat <<'EOF' +Usage: install.sh [options] + --condor Install Condor (routes to the existing deploy/setup.sh) + --hummingbot Install the Hummingbot client (coming in P2) + --upgrade Update an existing install in place + --doctor Run the health check and exit + --dir Base directory (default: $HOME) + -h, --help Show this help +EOF + exit 0 +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --condor) PRODUCT="condor" ;; + --hummingbot) PRODUCT="hummingbot" ;; + --upgrade) MODE="upgrade" ;; + --doctor) MODE="doctor" ;; + --dir) shift; BASE_DIR="${1:?--dir needs a path}"; API_DIR="$BASE_DIR/hummingbot-api"; CONDOR_DIR="$BASE_DIR/condor" ;; + -h|--help) usage ;; + *) warn "Ignoring unknown option: $1" ;; + esac + shift + done +} + +detect_platform() { + local platform; platform="$(uname -ms)" + case "$platform" in + Darwin\ arm64|Darwin\ x86_64) OS="darwin" ;; + Linux\ x86_64|Linux\ aarch64|Linux\ arm64) OS="linux" ;; + *) error "Unsupported platform: $platform. Windows users: run inside WSL2 (Windows support is coming)." ;; + esac + if [[ "$OS" == "linux" ]] && command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then + error "musl libc (Alpine) is not supported. Use a glibc-based distro." + fi +} + +require_cmd() { command -v "$1" >/dev/null 2>&1 || error "'$1' is required but not installed."; } + +# ── wizard ──────────────────────────────────────────────────────────────────── +choose_product() { + is_promptable || error "No product selected and no terminal available. Re-run with --condor or --hummingbot." + { + printf '%s\n' "${BOLD}What do you want to install?${NC}" + printf ' %s\n' "1) Condor — Telegram + AI trading agents (Docker)" + printf ' %s\n' "2) Hummingbot — the trading client / framework" + } > /dev/tty + local choice=""; prompt choice "Choose 1 or 2" "1" + case "$choice" in + 2|hummingbot) echo "hummingbot" ;; + *) echo "condor" ;; + esac +} + +# ── Condor: route to the existing installer (unchanged) ─────────────────────── +run_condor() { + step "Installing Condor (Telegram + AI trading agents)" + local args=(); [[ "$MODE" == "upgrade" ]] && args+=("--upgrade") + + # Prefer a sibling setup.sh when run from a deploy checkout; otherwise fetch the canonical one. + local setup; setup="$(sibling_setup)" + if [[ -n "$setup" ]]; then + info "Handing off to the existing Condor installer: $(tildify "$setup")" + else + info "Fetching the existing Condor installer (setup.sh)…" + setup="$(mktemp)"; curl -fsSL "$REPO_RAW/setup.sh" -o "$setup" \ + || error "Could not download the Condor installer." + fi + + # setup.sh reads prompts from stdin; under `curl | bash` hand it the terminal. + if is_promptable; then exec bash "$setup" "${args[@]}" < /dev/tty + else exec bash "$setup" "${args[@]}"; fi +} + +sibling_setup() { + local src="${BASH_SOURCE[0]:-}"; [[ -f "$src" ]] || return 0 + local dir; dir="$(cd "$(dirname "$src")" && pwd)" + [[ -f "$dir/setup.sh" ]] && printf '%s' "$dir/setup.sh" +} + +# ── Hummingbot client (P2) ──────────────────────────────────────────────────── +install_hummingbot() { + step "Hummingbot client" + warn "The Hummingbot client path arrives in the next installer release (P2)." + cat </dev/null 2>&1 && _ok "docker installed" || _bad "docker missing" "install Docker Desktop" + if command -v docker >/dev/null 2>&1; then + docker info >/dev/null 2>&1 && _ok "docker daemon running" || _bad "docker daemon down" "start Docker" + docker compose version >/dev/null 2>&1 && _ok "docker compose available" || _warn "docker compose missing" "install Compose v2" + fi + command -v uv >/dev/null 2>&1 && _ok "uv installed" || _warn "uv missing" "needed for Condor" + command -v tmux >/dev/null 2>&1 && _ok "tmux installed" || _warn "tmux missing" "needed to run Condor" + + if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^hummingbot-api$'; then + _ok "hummingbot-api container up" + else _warn "hummingbot-api not running" "cd $(tildify "$API_DIR") && docker compose up -d"; fi + if curl -fsS -o /dev/null --max-time 3 http://localhost:8000/docs 2>/dev/null; then + _ok "API reachable on :8000" + else _warn "API not answering on :8000" "give it a few seconds after first start"; fi + + if tmux has-session -t condor 2>/dev/null; then _ok "Condor running (attach: tmux attach -t condor)" + else _warn "Condor not running" "tmux session 'condor' not found"; fi + + printf '\n' + if [[ "$fails" -gt 0 ]]; then warn "$fails critical check(s) failed."; return 1; fi + success "No critical failures."; return 0 +} + +# Run — this MUST be the last line (truncation safety for curl | bash). +main "$@" From 7ef3eacad23bdbac856de42339e74dd2675c26f0 Mon Sep 17 00:00:00 2001 From: Michael Feng Date: Mon, 1 Jun 2026 16:33:10 -0700 Subject: [PATCH 2/4] Split installer into install-hummingbot.sh + install-condor.sh The website's Hummingbot/Condor selector routes to two single-purpose scripts (no in-script product wizard): - install-condor.sh: renamed from setup.sh, unchanged Condor + API flow. Self-references and README updated to the new name. - install-hummingbot.sh: new Hummingbot client installer. Docker (make setup/deploy) or source (make install/conda), latest|dev channel, installs a thin `hummingbot` wrapper (start/update/doctor) to ~/.local/bin, writes ~/.hummingbot/state.json, default-on LLM plugin notice, and --doctor. curl|bash-safe (main "$@" last line, /dev/tty prompts, platform/musl guards). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 12 +- setup.sh => install-condor.sh | 10 +- install-hummingbot.sh | 387 ++++++++++++++++++++++++++++++++++ install.sh | 218 ------------------- 4 files changed, 398 insertions(+), 229 deletions(-) rename setup.sh => install-condor.sh (99%) create mode 100755 install-hummingbot.sh delete mode 100755 install.sh diff --git a/README.md b/README.md index 86f50985e..d2d0855cf 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This folder helps you install **Condor** (a Telegram bot for trading) and, if yo Open Terminal, go to an **empty folder** where you are happy to create files (for example your home folder, or `cd Desktop` first), then paste: ```bash -curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash +curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/install-condor.sh | bash ``` The installer walks you through setup—for example your **Telegram** bot token and your **Telegram user id**—and can also install **Hummingbot API** on the **same machine** if you choose that when it asks. When it finishes, continue to **After installation** below. @@ -30,7 +30,7 @@ The installer walks you through setup—for example your **Telegram** bot token Use this when you are deploying **Hummingbot API** on its own machine (for example a **VPS** or another **remote server**), or any time you **only** need the API and database stack and **not** Condor. **Docker** must be installed and running on that server before you run the command: ```bash -curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash -s -- --hummingbot-api +curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/install-condor.sh | bash -s -- --hummingbot-api ``` --- @@ -42,7 +42,7 @@ curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | b **Manual — from the computer:** if you prefer to run the installer again yourself, go back to the **same folder** where you first installed (where the `condor` folder lives), open Terminal there, then paste: ```bash -curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/setup.sh | bash -s -- --upgrade +curl -fsSL https://raw.githubusercontent.com/hummingbot/deploy/main/install-condor.sh | bash -s -- --upgrade ``` --- @@ -104,9 +104,9 @@ cd hummingbot-api && make setup && docker compose pull && make deploy Use the same commands, but start with `bash` instead of `curl`: ```bash -bash setup.sh -bash setup.sh --hummingbot-api -bash setup.sh --upgrade +bash install-condor.sh +bash install-condor.sh --hummingbot-api +bash install-condor.sh --upgrade ``` --- diff --git a/setup.sh b/install-condor.sh similarity index 99% rename from setup.sh rename to install-condor.sh index 81fc8228b..6879713e2 100755 --- a/setup.sh +++ b/install-condor.sh @@ -18,7 +18,7 @@ set -eu CONDOR_REPO="https://github.com/hummingbot/condor.git" API_REPO="https://github.com/hummingbot/hummingbot-api.git" # Used in printed hints when the script has no file path (e.g. curl | bash) -DEPLOY_SETUP_RAW_URL="https://raw.githubusercontent.com/hummingbot/deploy/refs/heads/main/setup.sh" +DEPLOY_SETUP_RAW_URL="https://raw.githubusercontent.com/hummingbot/deploy/refs/heads/main/install-condor.sh" CONDOR_DIR="condor" API_DIR="hummingbot-api" DOCKER_COMPOSE="" @@ -169,16 +169,16 @@ while [[ $# -gt 0 ]]; do esac done -# Absolute path for "bash /path/setup.sh …" hints. Empty when piped: `curl … | bash` leaves -# BASH_SOURCE unset, and `set -u` would error on ${BASH_SOURCE[0]}. +# Absolute path for "bash /path/install-condor.sh …" hints. Empty when piped: `curl … | bash` +# leaves BASH_SOURCE unset, and `set -u` would error on ${BASH_SOURCE[0]}. deploy_resolve_script_abs() { local s="${BASH_SOURCE[0]:-}" if [[ -n "$s" && -f "$s" ]]; then echo "$(cd "$(dirname "$s")" >/dev/null 2>&1 && pwd)/$(basename "$s")" return fi - if [[ -f "$(pwd)/setup.sh" ]]; then - echo "$(pwd)/setup.sh" + if [[ -f "$(pwd)/install-condor.sh" ]]; then + echo "$(pwd)/install-condor.sh" return fi echo "" diff --git a/install-hummingbot.sh b/install-hummingbot.sh new file mode 100755 index 000000000..44b72a140 --- /dev/null +++ b/install-hummingbot.sh @@ -0,0 +1,387 @@ +#!/usr/bin/env bash +# +# install-hummingbot.sh — install the Hummingbot client (Docker or source) + the `hummingbot` wrapper. +# +# The website's "Hummingbot" tab points here; the Hummingbot-vs-Condor choice happens in the UI, +# so this script only installs Hummingbot. (Condor has its own installer: install-condor.sh.) +# +# curl -fsSL https://hummingbot.org/install-hummingbot.sh | bash # interactive +# curl -fsSL https://hummingbot.org/install-hummingbot.sh | bash -s -- --docker +# curl -fsSL https://hummingbot.org/install-hummingbot.sh | bash -s -- --source --dev +# curl -fsSL https://hummingbot.org/install-hummingbot.sh | bash -s -- --doctor +# +# Env overrides: HUMMINGBOT_INSTALL (base dir, default $HOME). +# See apps/docs/INSTALL_WIZARD_PLAN.md in hummingbot/hummingbot-web. + +main() { + set -euo pipefail + + REPO="${GITHUB_BASE:-https://github.com}/hummingbot/hummingbot.git" + BASE_DIR="${HUMMINGBOT_INSTALL:-$HOME}" + HB_DIR="$BASE_DIR/hummingbot" + STATE_DIR="$HOME/.hummingbot" + BIN_DIR="$HOME/.local/bin" + + METHOD="" # docker | source | develop (empty -> ask) + MODE="install" # install | doctor + CHANNEL="latest" # latest | dev + INSTALL_PLUGIN=1 + ASSUME_YES=0 + + ui_init + parse_args "$@" + [[ "$MODE" == "doctor" ]] && { run_doctor; exit $?; } + + banner + detect_platform + require_cmd curl + require_cmd git + + [[ -z "$METHOD" ]] && METHOD="$(choose_method)" + case "$METHOD" in + docker) install_docker ;; + source) install_source ;; + develop) install_develop ;; + *) error "Unknown method: $METHOD" ;; + esac +} + +# ── UI helpers ──────────────────────────────────────────────────────────────── +ui_init() { + if [[ -t 1 ]]; then + BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[0;31m'; GRN=$'\033[0;32m' + YEL=$'\033[1;33m'; CYN=$'\033[0;36m'; NC=$'\033[0m' + else + BOLD=""; DIM=""; RED=""; GRN=""; YEL=""; CYN=""; NC="" + fi +} +info() { printf '%s\n' "${CYN}→${NC} $*"; } +warn() { printf '%s\n' "${YEL}!${NC} $*" >&2; } +success() { printf '%s\n' "${GRN}✓${NC} $*"; } +error() { printf '%s\n' "${RED}✗ $*${NC}" >&2; exit 1; } +step() { printf '\n%s\n' "${BOLD}$*${NC}"; } +tildify() { printf '%s' "${1/#$HOME/~}"; } + +banner() { + printf '\n%s\n%s\n\n' "${BOLD}🐦 Hummingbot installer${NC}" "${DIM}the open-source client for agentic trading${NC}" +} + +is_promptable() { [[ -t 1 && -r /dev/tty ]]; } + +prompt() { # prompt VAR "Question" "default" + local __var="$1" __q="$2" __def="${3:-}" __ans="" + if ! is_promptable; then + [[ -n "$__def" ]] && { printf -v "$__var" '%s' "$__def"; return 0; } + error "Need a value for '$__q' but no terminal is available." + fi + if [[ -n "$__def" ]]; then printf '%s ' "${CYN}?${NC} $__q ${DIM}[$__def]${NC}:" > /dev/tty + else printf '%s ' "${CYN}?${NC} $__q:" > /dev/tty; fi + read -r __ans < /dev/tty || true + printf -v "$__var" '%s' "${__ans:-$__def}" +} + +# ── args / platform / deps ──────────────────────────────────────────────────── +usage() { + cat <<'EOF' +Usage: install-hummingbot.sh [options] + --docker Run the client via Docker (recommended) + --source Build and run the client from source (conda) + --develop Develop core / API / Gateway from source (coming in P3) + --dev Use the development channel (default: latest) + --no-plugin Skip the default Claude/LLM plugin + --dir Base directory (default: $HOME) + --doctor Run the health check and exit + -y, --yes Assume yes to prompts + -h, --help Show this help +EOF + exit 0 +} + +parse_args() { + while [[ $# -gt 0 ]]; do + case "$1" in + --docker) METHOD="docker" ;; + --source) METHOD="source" ;; + --develop) METHOD="develop" ;; + --dev) CHANNEL="dev" ;; + --latest) CHANNEL="latest" ;; + --no-plugin) INSTALL_PLUGIN=0 ;; + --dir) shift; BASE_DIR="${1:?--dir needs a path}"; HB_DIR="$BASE_DIR/hummingbot" ;; + --doctor) MODE="doctor" ;; + -y|--yes) ASSUME_YES=1 ;; + -h|--help) usage ;; + *) warn "Ignoring unknown option: $1" ;; + esac + shift + done +} + +detect_platform() { + local platform; platform="$(uname -ms)" + case "$platform" in + Darwin\ arm64|Darwin\ x86_64) OS="darwin" ;; + Linux\ x86_64|Linux\ aarch64|Linux\ arm64) OS="linux" ;; + *) error "Unsupported platform: $platform. Windows users: run inside WSL2 (Windows support is coming)." ;; + esac + if [[ "$OS" == "linux" ]] && command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then + error "musl libc (Alpine) is not supported. Use a glibc-based distro." + fi +} + +require_cmd() { command -v "$1" >/dev/null 2>&1 || error "'$1' is required but not installed."; } + +ensure_docker() { + command -v docker >/dev/null 2>&1 || error "Docker is required. Install Docker Desktop: https://docker.com/products/docker-desktop" + docker info >/dev/null 2>&1 || error "Docker is installed but the daemon isn't running. Start Docker and re-run." + docker compose version >/dev/null 2>&1 || error "Docker Compose v2 is required (docker compose)." +} + +# Find conda, or install Miniforge. Leaves `conda` on PATH for the Makefile. +ensure_conda() { + if ! command -v conda >/dev/null 2>&1; then + local c + for c in "$HOME/miniforge3" "$HOME/miniconda3" "$HOME/anaconda3" "/opt/conda"; do + if [[ -f "$c/etc/profile.d/conda.sh" ]]; then . "$c/etc/profile.d/conda.sh"; break; fi + done + fi + if ! command -v conda >/dev/null 2>&1; then + info "No conda found — installing Miniforge…" + local mf; mf="$(mktemp)" + curl -fsSL -o "$mf" "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" \ + || error "Failed to download Miniforge." + bash "$mf" -b -p "$HOME/miniforge3" || error "Miniforge install failed." + . "$HOME/miniforge3/etc/profile.d/conda.sh" + fi + command -v conda >/dev/null 2>&1 || error "conda is not available." + export PATH="$(conda info --base)/bin:$PATH" # ensure `conda` is on PATH for make +} + +# Run a native command, giving it the terminal when we have one (Makefiles sometimes prompt). +run_native() { if is_promptable; then "$@" < /dev/tty; else "$@"; fi; } + +clone_or_update() { # clone_or_update + local branch="$1" + if [[ -d "$HB_DIR/.git" ]]; then + info "Updating $(tildify "$HB_DIR")…" + git -C "$HB_DIR" fetch --depth 1 origin "$branch" 2>/dev/null || git -C "$HB_DIR" fetch origin 2>/dev/null || true + git -C "$HB_DIR" checkout "$branch" 2>/dev/null || true + git -C "$HB_DIR" pull --ff-only 2>/dev/null || warn "Could not fast-forward; leaving checkout as-is." + else + info "Cloning Hummingbot ($branch) into $(tildify "$HB_DIR")…" + git clone --depth 1 --branch "$branch" "$REPO" "$HB_DIR" 2>/dev/null \ + || git clone "$REPO" "$HB_DIR" || error "Failed to clone $REPO" + git -C "$HB_DIR" checkout "$branch" 2>/dev/null || true + fi +} + +branch_for_channel() { [[ "$CHANNEL" == "dev" ]] && echo "development" || echo "master"; } + +# ── method wizard ───────────────────────────────────────────────────────────── +choose_method() { + is_promptable || error "No method selected and no terminal available. Re-run with --docker or --source." + { + printf '%s\n' "${BOLD}How do you want to run Hummingbot?${NC}" + printf ' %s\n' "1) Docker — run the client in a container (recommended)" + printf ' %s\n' "2) Source — build from source with conda" + printf ' %s\n' "3) Develop — core / API / Gateway from source (coming in P3)" + } > /dev/tty + local choice=""; prompt choice "Choose 1, 2 or 3" "1" + case "$choice" in + 2|source) echo "source" ;; + 3|develop) echo "develop" ;; + *) echo "docker" ;; + esac +} + +# ── Docker install ──────────────────────────────────────────────────────────── +install_docker() { + step "Installing Hummingbot client (Docker, channel: $CHANNEL)" + ensure_docker + mkdir -p "$BASE_DIR" + clone_or_update "$(branch_for_channel)" + + if [[ "$CHANNEL" == "dev" && -f "$HB_DIR/docker-compose.yml" ]]; then + info "Pinning image to :development…" + sed -i.bak -E 's#(image:[[:space:]]*hummingbot/hummingbot):[^[:space:]]+#\1:development#' "$HB_DIR/docker-compose.yml" \ + && rm -f "$HB_DIR/docker-compose.yml.bak" + fi + + info "Configuring (make setup)…" + ( cd "$HB_DIR" && run_native make setup ) + info "Pulling image + starting the container (make deploy)…" + ( cd "$HB_DIR" && make deploy ) || error "make deploy failed." + + finish "docker" +} + +# ── Source install ──────────────────────────────────────────────────────────── +install_source() { + step "Installing Hummingbot client (source, channel: $CHANNEL)" + ensure_conda + mkdir -p "$BASE_DIR" + clone_or_update "$(branch_for_channel)" + + info "Building the conda env + compiling (make install — this takes a while)…" + ( cd "$HB_DIR" && run_native make install ) || error "make install failed." + + finish "source" +} + +# ── Develop (P3 stub) ───────────────────────────────────────────────────────── +install_develop() { + step "Develop Hummingbot / API / Gateway" + warn "The multi-component developer path arrives in P3." + cat < + METHOD="$1" + install_wrapper + write_state + maybe_install_plugin + run_doctor || true + print_next_steps +} + +install_wrapper() { + mkdir -p "$BIN_DIR" + info "Installing the ${BOLD}hummingbot${NC} command → $(tildify "$BIN_DIR/hummingbot")" + cat > "$BIN_DIR/hummingbot" <<'WRAP' +#!/usr/bin/env bash +# hummingbot — thin wrapper installed by install-hummingbot.sh (start / update / doctor). +set -euo pipefail +STATE="$HOME/.hummingbot/state.json" +[[ -f "$STATE" ]] || { echo "No Hummingbot install found. Run install-hummingbot.sh first." >&2; exit 1; } +jget() { sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$STATE" | head -1; } +METHOD="$(jget method)"; BASE_DIR="$(jget base_dir)"; CHANNEL="$(jget channel)" +HB_DIR="$BASE_DIR/hummingbot" + +cmd="${1:-start}"; [[ $# -gt 0 ]] && shift || true +case "$cmd" in + start) + case "$METHOD" in + source) cd "$HB_DIR"; exec conda run -n hummingbot --no-capture-output ./bin/hummingbot_quickstart.py "$@" ;; + docker) docker start hummingbot >/dev/null 2>&1 || true; exec docker attach hummingbot ;; + *) echo "Unknown method '$METHOD'." >&2; exit 1 ;; + esac ;; + update) + for a in "$@"; do case "$a" in --dev) CHANNEL=dev ;; --latest) CHANNEL=latest ;; esac; done + case "$METHOD" in + source) + cd "$HB_DIR" + [[ "$CHANNEL" == "dev" ]] && git checkout development 2>/dev/null || true + git pull --ff-only && make install ;; + docker) + cd "$HB_DIR"; docker compose pull && docker compose up -d ;; + esac + echo "✓ Updated ($METHOD, channel: $CHANNEL)." ;; + doctor) + case "$METHOD" in + source) conda env list 2>/dev/null | awk '{print $1}' | grep -qx hummingbot \ + && echo "✓ hummingbot conda env present" || echo "✗ hummingbot conda env missing — run install-hummingbot.sh --source" ;; + docker) docker ps --format '{{.Names}}' 2>/dev/null | grep -qx hummingbot \ + && echo "✓ hummingbot container running" || echo "! hummingbot container not running — run: hummingbot start" ;; + esac ;; + -v|--version|version) + [[ -f "$HB_DIR/hummingbot/VERSION" ]] && cat "$HB_DIR/hummingbot/VERSION" || echo "unknown" ;; + *) echo "usage: hummingbot {start|update [--dev|--latest]|doctor|--version}" >&2; exit 1 ;; +esac +WRAP + chmod +x "$BIN_DIR/hummingbot" + ensure_path +} + +# Add ~/.local/bin to PATH (idempotent, shell-aware). +ensure_path() { + case ":$PATH:" in *":$BIN_DIR:"*) return 0 ;; esac + local rc line="export PATH=\"\$HOME/.local/bin:\$PATH\"" + case "$(basename "${SHELL:-}")" in + zsh) rc="${ZDOTDIR:-$HOME}/.zshrc" ;; + bash) [[ "$OS" == "darwin" ]] && rc="$HOME/.bash_profile" || rc="$HOME/.bashrc" ;; + *) rc="$HOME/.profile" ;; + esac + if [[ -w "$(dirname "$rc")" ]] && ! { [[ -f "$rc" ]] && grep -qF "$BIN_DIR" "$rc"; }; then + { printf '\n# Hummingbot\n%s\n' "$line"; } >> "$rc" + info "Added ~/.local/bin to PATH in $(tildify "$rc") — open a new shell or: ${BOLD}source $(tildify "$rc")${NC}" + fi + export PATH="$BIN_DIR:$PATH" +} + +write_state() { + mkdir -p "$STATE_DIR" + cat > "$STATE_DIR/state.json" </dev/null 2>&1 && _ok "git installed" || _bad "git missing" "install git" + [[ -d "$HB_DIR/.git" ]] && _ok "Hummingbot checkout present ($(tildify "$HB_DIR"))" \ + || _warn "no checkout at $(tildify "$HB_DIR")" "run install-hummingbot.sh" + + if command -v conda >/dev/null 2>&1 && conda env list 2>/dev/null | awk '{print $1}' | grep -qx hummingbot; then + _ok "hummingbot conda env present (source mode)" + elif command -v docker >/dev/null 2>&1; then + docker info >/dev/null 2>&1 && _ok "docker daemon running" || _warn "docker daemon down" "start Docker for the container client" + docker ps --format '{{.Names}}' 2>/dev/null | grep -qx hummingbot && _ok "hummingbot container running" \ + || _warn "hummingbot container not running" "hummingbot start" + else + _warn "neither conda env nor docker found" "install via --source or --docker" + fi + + command -v hummingbot >/dev/null 2>&1 && _ok "hummingbot command on PATH" \ + || _warn "hummingbot not on PATH yet" "open a new shell, or source your shell rc" + + printf '\n' + if [[ "$fails" -gt 0 ]]; then warn "$fails critical check(s) failed."; return 1; fi + success "No critical failures."; return 0 +} + +# ── next steps ──────────────────────────────────────────────────────────────── +print_next_steps() { + cat < -p [--headless] + ${BOLD}Update${NC} hummingbot update ${DIM}(switch channel: --dev / --latest)${NC} + ${BOLD}Check${NC} hummingbot doctor + + ${DIM}If 'hummingbot' isn't found, open a new terminal (or source your shell rc).${NC} + +EOF +} + +# Run — this MUST be the last line (truncation safety for curl | bash). +main "$@" diff --git a/install.sh b/install.sh deleted file mode 100755 index 8eee9d1fe..000000000 --- a/install.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env bash -# -# Hummingbot installer — one entry point to install Hummingbot or Condor. -# -# curl -fsSL https://hummingbot.org/install.sh | bash # interactive wizard -# curl -fsSL https://hummingbot.org/install.sh | bash -s -- --condor # Condor (existing process) -# curl -fsSL https://hummingbot.org/install.sh | bash -s -- --doctor # health check only -# -# The user picks Hummingbot or Condor (in the website UI, or this wizard). -# • Condor → routed to the EXISTING, unchanged deploy/setup.sh. We don't reimplement it. -# • Hummingbot → the client install + `hummingbot` wrapper + LLM plugin (built out in P2). -# -# See apps/docs/INSTALL_WIZARD_PLAN.md in hummingbot/hummingbot-web. - -main() { - set -euo pipefail - - REPO_RAW="${DEPLOY_RAW_BASE:-https://raw.githubusercontent.com/hummingbot/deploy/main}" - BASE_DIR="${HUMMINGBOT_INSTALL:-$HOME}" - API_DIR="$BASE_DIR/hummingbot-api" - CONDOR_DIR="$BASE_DIR/condor" - STATE_DIR="$HOME/.hummingbot" - - PRODUCT="" # condor | hummingbot (empty -> ask) - MODE="install" # install | upgrade | doctor - - ui_init - parse_args "$@" - - [[ "$MODE" == "doctor" ]] && { run_doctor; exit $?; } - - banner - detect_platform - require_cmd curl - - [[ -z "$PRODUCT" ]] && PRODUCT="$(choose_product)" - case "$PRODUCT" in - condor) run_condor ;; # exec's the existing installer — never returns - hummingbot) install_hummingbot ;; - *) error "Unknown product: $PRODUCT" ;; - esac -} - -# ── UI helpers ──────────────────────────────────────────────────────────────── -ui_init() { - if [[ -t 1 ]]; then - BOLD=$'\033[1m'; DIM=$'\033[2m'; RED=$'\033[0;31m'; GRN=$'\033[0;32m' - YEL=$'\033[1;33m'; CYN=$'\033[0;36m'; NC=$'\033[0m' - else - BOLD=""; DIM=""; RED=""; GRN=""; YEL=""; CYN=""; NC="" - fi -} -info() { printf '%s\n' "${CYN}→${NC} $*"; } -warn() { printf '%s\n' "${YEL}!${NC} $*" >&2; } -success() { printf '%s\n' "${GRN}✓${NC} $*"; } -error() { printf '%s\n' "${RED}✗ $*${NC}" >&2; exit 1; } -step() { printf '\n%s\n' "${BOLD}$*${NC}"; } -tildify() { printf '%s' "${1/#$HOME/~}"; } # replacement '~' is literal; no tilde expansion here - -banner() { - printf '\n%s\n%s\n\n' "${BOLD}🐦 Hummingbot installer${NC}" "${DIM}open-source framework for agentic trading${NC}" -} - -# Under `curl | bash`, stdin is the script — interactive input must come from /dev/tty. -is_promptable() { [[ -t 1 && -r /dev/tty ]]; } - -# prompt VAR "Question" "default" -prompt() { - local __var="$1" __q="$2" __def="${3:-}" __ans="" - if ! is_promptable; then - [[ -n "$__def" ]] && { printf -v "$__var" '%s' "$__def"; return 0; } - error "Need a value for '$__q' but no terminal is available." - fi - if [[ -n "$__def" ]]; then printf '%s ' "${CYN}?${NC} $__q ${DIM}[$__def]${NC}:" > /dev/tty - else printf '%s ' "${CYN}?${NC} $__q:" > /dev/tty; fi - read -r __ans < /dev/tty || true - printf -v "$__var" '%s' "${__ans:-$__def}" -} - -# ── args / platform ─────────────────────────────────────────────────────────── -usage() { - cat <<'EOF' -Usage: install.sh [options] - --condor Install Condor (routes to the existing deploy/setup.sh) - --hummingbot Install the Hummingbot client (coming in P2) - --upgrade Update an existing install in place - --doctor Run the health check and exit - --dir Base directory (default: $HOME) - -h, --help Show this help -EOF - exit 0 -} - -parse_args() { - while [[ $# -gt 0 ]]; do - case "$1" in - --condor) PRODUCT="condor" ;; - --hummingbot) PRODUCT="hummingbot" ;; - --upgrade) MODE="upgrade" ;; - --doctor) MODE="doctor" ;; - --dir) shift; BASE_DIR="${1:?--dir needs a path}"; API_DIR="$BASE_DIR/hummingbot-api"; CONDOR_DIR="$BASE_DIR/condor" ;; - -h|--help) usage ;; - *) warn "Ignoring unknown option: $1" ;; - esac - shift - done -} - -detect_platform() { - local platform; platform="$(uname -ms)" - case "$platform" in - Darwin\ arm64|Darwin\ x86_64) OS="darwin" ;; - Linux\ x86_64|Linux\ aarch64|Linux\ arm64) OS="linux" ;; - *) error "Unsupported platform: $platform. Windows users: run inside WSL2 (Windows support is coming)." ;; - esac - if [[ "$OS" == "linux" ]] && command -v ldd >/dev/null 2>&1 && ldd --version 2>&1 | grep -qi musl; then - error "musl libc (Alpine) is not supported. Use a glibc-based distro." - fi -} - -require_cmd() { command -v "$1" >/dev/null 2>&1 || error "'$1' is required but not installed."; } - -# ── wizard ──────────────────────────────────────────────────────────────────── -choose_product() { - is_promptable || error "No product selected and no terminal available. Re-run with --condor or --hummingbot." - { - printf '%s\n' "${BOLD}What do you want to install?${NC}" - printf ' %s\n' "1) Condor — Telegram + AI trading agents (Docker)" - printf ' %s\n' "2) Hummingbot — the trading client / framework" - } > /dev/tty - local choice=""; prompt choice "Choose 1 or 2" "1" - case "$choice" in - 2|hummingbot) echo "hummingbot" ;; - *) echo "condor" ;; - esac -} - -# ── Condor: route to the existing installer (unchanged) ─────────────────────── -run_condor() { - step "Installing Condor (Telegram + AI trading agents)" - local args=(); [[ "$MODE" == "upgrade" ]] && args+=("--upgrade") - - # Prefer a sibling setup.sh when run from a deploy checkout; otherwise fetch the canonical one. - local setup; setup="$(sibling_setup)" - if [[ -n "$setup" ]]; then - info "Handing off to the existing Condor installer: $(tildify "$setup")" - else - info "Fetching the existing Condor installer (setup.sh)…" - setup="$(mktemp)"; curl -fsSL "$REPO_RAW/setup.sh" -o "$setup" \ - || error "Could not download the Condor installer." - fi - - # setup.sh reads prompts from stdin; under `curl | bash` hand it the terminal. - if is_promptable; then exec bash "$setup" "${args[@]}" < /dev/tty - else exec bash "$setup" "${args[@]}"; fi -} - -sibling_setup() { - local src="${BASH_SOURCE[0]:-}"; [[ -f "$src" ]] || return 0 - local dir; dir="$(cd "$(dirname "$src")" && pwd)" - [[ -f "$dir/setup.sh" ]] && printf '%s' "$dir/setup.sh" -} - -# ── Hummingbot client (P2) ──────────────────────────────────────────────────── -install_hummingbot() { - step "Hummingbot client" - warn "The Hummingbot client path arrives in the next installer release (P2)." - cat </dev/null 2>&1 && _ok "docker installed" || _bad "docker missing" "install Docker Desktop" - if command -v docker >/dev/null 2>&1; then - docker info >/dev/null 2>&1 && _ok "docker daemon running" || _bad "docker daemon down" "start Docker" - docker compose version >/dev/null 2>&1 && _ok "docker compose available" || _warn "docker compose missing" "install Compose v2" - fi - command -v uv >/dev/null 2>&1 && _ok "uv installed" || _warn "uv missing" "needed for Condor" - command -v tmux >/dev/null 2>&1 && _ok "tmux installed" || _warn "tmux missing" "needed to run Condor" - - if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^hummingbot-api$'; then - _ok "hummingbot-api container up" - else _warn "hummingbot-api not running" "cd $(tildify "$API_DIR") && docker compose up -d"; fi - if curl -fsS -o /dev/null --max-time 3 http://localhost:8000/docs 2>/dev/null; then - _ok "API reachable on :8000" - else _warn "API not answering on :8000" "give it a few seconds after first start"; fi - - if tmux has-session -t condor 2>/dev/null; then _ok "Condor running (attach: tmux attach -t condor)" - else _warn "Condor not running" "tmux session 'condor' not found"; fi - - printf '\n' - if [[ "$fails" -gt 0 ]]; then warn "$fails critical check(s) failed."; return 1; fi - success "No critical failures."; return 0 -} - -# Run — this MUST be the last line (truncation safety for curl | bash). -main "$@" From 9f4ec48b86ce2ca43b77f5eb41276bb9d45f5239 Mon Sep 17 00:00:00 2001 From: Michael Feng Date: Mon, 1 Jun 2026 16:44:03 -0700 Subject: [PATCH 3/4] =?UTF-8?q?Add=20setup.sh=20back-compat=20shim=20?= =?UTF-8?q?=E2=86=92=20install-condor.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeps existing 'curl .../deploy/main/setup.sh | bash' links working after the rename: uses the sibling install-condor.sh from a checkout, or fetches it when piped, forwarding all args and /dev/tty. Co-Authored-By: Claude Opus 4.8 (1M context) --- setup.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 setup.sh diff --git a/setup.sh b/setup.sh new file mode 100755 index 000000000..49d958486 --- /dev/null +++ b/setup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# setup.sh — back-compat shim. +# +# The Condor installer was renamed to install-condor.sh. This shim forwards to it so existing +# `curl -fsSL .../deploy/main/setup.sh | bash` links keep working. Prefer install-condor.sh directly. + +set -euo pipefail + +# If we're running from a checkout, use the sibling install-condor.sh. +src="${BASH_SOURCE[0]:-}" +if [[ -n "$src" && -f "$src" ]]; then + dir="$(cd "$(dirname "$src")" && pwd)" + if [[ -f "$dir/install-condor.sh" ]]; then + exec bash "$dir/install-condor.sh" "$@" + fi +fi + +# Piped via curl | bash: fetch the renamed installer and run it. +tmp="$(mktemp)"; trap 'rm -f "$tmp"' EXIT INT TERM +curl -fsSL "https://raw.githubusercontent.com/hummingbot/deploy/main/install-condor.sh" -o "$tmp" \ + || { echo "Could not fetch install-condor.sh — see https://github.com/hummingbot/deploy" >&2; exit 1; } +if [[ -t 1 && -r /dev/tty ]]; then exec bash "$tmp" "$@" < /dev/tty; else exec bash "$tmp" "$@"; fi From 2bfe637bf119f1957fffe98f4171d2dfffb442ef Mon Sep 17 00:00:00 2001 From: Michael Feng Date: Mon, 1 Jun 2026 22:41:50 -0700 Subject: [PATCH 4/4] install-hummingbot: finalize the hummingbot CLI wrapper Make the `hummingbot` shim's `update` actually channel-aware: check out the right source branch (master/development) or re-pin the docker image tag on --latest/--dev, and persist the chosen channel back to state.json so it sticks. Add `help`/`-h`/`--help` usage and tighten the source doctor to exit non-zero when the conda env is missing. Co-Authored-By: Claude Opus 4.8 (1M context) --- install-hummingbot.sh | 52 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/install-hummingbot.sh b/install-hummingbot.sh index 44b72a140..edd96e44f 100755 --- a/install-hummingbot.sh +++ b/install-hummingbot.sh @@ -257,43 +257,79 @@ install_wrapper() { info "Installing the ${BOLD}hummingbot${NC} command → $(tildify "$BIN_DIR/hummingbot")" cat > "$BIN_DIR/hummingbot" <<'WRAP' #!/usr/bin/env bash -# hummingbot — thin wrapper installed by install-hummingbot.sh (start / update / doctor). +# hummingbot — thin wrapper installed by install-hummingbot.sh. +# Verbs: start · update · doctor · --version. It resolves *where* to run +# bin/hummingbot_quickstart.py (source conda env / docker container) and hides +# conda activate / docker attach. Not a lifecycle CLI — see INSTALL_WIZARD_PLAN.md. set -euo pipefail + STATE="$HOME/.hummingbot/state.json" [[ -f "$STATE" ]] || { echo "No Hummingbot install found. Run install-hummingbot.sh first." >&2; exit 1; } + jget() { sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$STATE" | head -1; } +jset() { sed -i.bak "s/\"$1\"[[:space:]]*:[[:space:]]*\"[^\"]*\"/\"$1\": \"$2\"/" "$STATE" && rm -f "$STATE.bak"; } + METHOD="$(jget method)"; BASE_DIR="$(jget base_dir)"; CHANNEL="$(jget channel)" HB_DIR="$BASE_DIR/hummingbot" +branch_for() { [[ "$1" == dev ]] && echo development || echo master; } # source branch +tag_for() { [[ "$1" == dev ]] && echo development || echo latest; } # docker image tag + +usage() { + cat </dev/null 2>&1 || true; exec docker attach hummingbot ;; - *) echo "Unknown method '$METHOD'." >&2; exit 1 ;; + *) echo "Unknown method '$METHOD' in $STATE." >&2; exit 1 ;; esac ;; update) for a in "$@"; do case "$a" in --dev) CHANNEL=dev ;; --latest) CHANNEL=latest ;; esac; done case "$METHOD" in source) cd "$HB_DIR" - [[ "$CHANNEL" == "dev" ]] && git checkout development 2>/dev/null || true - git pull --ff-only && make install ;; + b="$(branch_for "$CHANNEL")" + git fetch --depth 1 origin "$b" 2>/dev/null || git fetch origin 2>/dev/null || true + git checkout "$b" 2>/dev/null || git checkout -B "$b" "origin/$b" 2>/dev/null || true + git pull --ff-only 2>/dev/null || true + make install ;; docker) - cd "$HB_DIR"; docker compose pull && docker compose up -d ;; + cd "$HB_DIR" + if [[ -f docker-compose.yml ]]; then + sed -i.bak -E "s#(image:[[:space:]]*hummingbot/hummingbot):[^[:space:]]+#\1:$(tag_for "$CHANNEL")#" docker-compose.yml \ + && rm -f docker-compose.yml.bak + fi + docker compose pull && docker compose up -d ;; + *) echo "Unknown method '$METHOD'." >&2; exit 1 ;; esac + jset channel "$CHANNEL" echo "✓ Updated ($METHOD, channel: $CHANNEL)." ;; doctor) case "$METHOD" in source) conda env list 2>/dev/null | awk '{print $1}' | grep -qx hummingbot \ - && echo "✓ hummingbot conda env present" || echo "✗ hummingbot conda env missing — run install-hummingbot.sh --source" ;; + && echo "✓ hummingbot conda env present" \ + || { echo "✗ hummingbot conda env missing — run: install-hummingbot.sh --source" >&2; exit 1; } ;; docker) docker ps --format '{{.Names}}' 2>/dev/null | grep -qx hummingbot \ - && echo "✓ hummingbot container running" || echo "! hummingbot container not running — run: hummingbot start" ;; + && echo "✓ hummingbot container running" \ + || echo "! hummingbot container not running — run: hummingbot start" ;; esac ;; -v|--version|version) [[ -f "$HB_DIR/hummingbot/VERSION" ]] && cat "$HB_DIR/hummingbot/VERSION" || echo "unknown" ;; - *) echo "usage: hummingbot {start|update [--dev|--latest]|doctor|--version}" >&2; exit 1 ;; + -h|--help|help) usage ;; + *) echo "unknown command: $cmd" >&2; usage >&2; exit 1 ;; esac WRAP chmod +x "$BIN_DIR/hummingbot"