#!/usr/bin/env bash # ╔════════════════════════════════════════════════════════════════════════════╗ # ║ DB-GPT Quick Installer ║ # ║ ║ # ║ One-line usage: ║ # ║ curl -fsSL https://raw.githubusercontent.com/eosphoros-ai/DB-GPT/\ ║ # ║ main/scripts/install/install.sh | bash ║ # ║ ║ # ║ What this script does: ║ # ║ 1. Detect OS (macOS / Linux) ║ # ║ 2. Ensure git and curl are available ║ # ║ 3. Install uv (if not already present) ║ # ║ 4. Clone (or update) the DB-GPT repository ║ # ║ 5. Run `uv sync` with the extras for the chosen profile ║ # ║ 6. Generate a user config from template ║ # ║ 7. Print next-steps (start command, URL) ║ # ║ ║ # ║ What this script does NOT do: ║ # ║ - Install GPU drivers or CUDA ║ # ║ - Download large local models ║ # ║ - Modify shell rc files (except via uv installer) ║ # ║ - Collect telemetry ║ # ╚════════════════════════════════════════════════════════════════════════════╝ set -euo pipefail # ── Resolve script location (works for both local and curl|bash) ───────────── # When piped from curl, BASH_SOURCE is empty. In that case we download the # helper libs from GitHub on the fly. SCRIPT_DIR="" REMOTE_BASE="https://raw.githubusercontent.com/eosphoros-ai/DB-GPT/main/scripts/install" _resolve_script_dir() { if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "bash" ]]; then SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" fi } _resolve_script_dir # ── Source helper libs ──────────────────────────────────────────────────────── _source_lib() { local name="$1" if [[ -n "${SCRIPT_DIR}" && -f "${SCRIPT_DIR}/lib/${name}" ]]; then # shellcheck source=/dev/null source "${SCRIPT_DIR}/lib/${name}" else # Running via curl | bash — fetch from GitHub local tmp tmp="$(mktemp)" curl -fsSL "${REMOTE_BASE}/lib/${name}" -o "${tmp}" # shellcheck source=/dev/null source "${tmp}" rm -f "${tmp}" fi } _source_lib "common.sh" _source_lib "profiles.sh" # ── Fetch template file ────────────────────────────────────────────────────── # Returns the local path to the template. Downloads from GitHub when needed. _resolve_template() { local name="$1" if [[ -n "${SCRIPT_DIR}" && -f "${SCRIPT_DIR}/templates/${name}" ]]; then echo "${SCRIPT_DIR}/templates/${name}" else local tmp tmp="$(mktemp)" curl -fsSL "${REMOTE_BASE}/templates/${name}" -o "${tmp}" echo "${tmp}" fi } # ── Default values ──────────────────────────────────────────────────────────── PROFILE="" INSTALL_DIR="${DBGPT_INSTALL_DIR:-${HOME}/.dbgpt}" VERSION="${DBGPT_VERSION:-main}" REPO_DIR="${DBGPT_REPO_DIR:-}" USE_EXISTING_REPO="false" MIRROR="" YES="false" START_AFTER_INSTALL="false" # ── Usage ───────────────────────────────────────────────────────────────────── usage() { cat <<'EOF' DB-GPT Quick Installer Usage: install.sh [options] Options: --profile Deployment profile (currently: openai, kimi, minimax) --install-dir Where to install (default: ~/.dbgpt) --version Git tag or branch to check out (default: main) --repo-dir Use an existing local DB-GPT checkout --mirror china Use China PyPI mirror (Tsinghua) --yes Non-interactive — accept all defaults --start Start DB-GPT server after install -h, --help Show this help Environment variables: OPENAI_API_KEY Automatically injected into OpenAI config DBGPT_INSTALL_DIR Override default install directory DBGPT_REPO_DIR Reuse an existing local DB-GPT repo DBGPT_VERSION Override default git ref Examples: # Interactive (will ask which profile) bash install.sh # Fully non-interactive bash install.sh --profile openai --yes # With OpenAI API key OPENAI_API_KEY=sk-xxx bash install.sh --profile openai --yes # With Kimi / Moonshot API key MOONSHOT_API_KEY=sk-xxx bash install.sh --profile kimi --yes # With Minimax API key MINIMAX_API_KEY=sk-xxx bash install.sh --profile minimax --yes # Reuse your current local repo (skip clone/update) OPENAI_API_KEY=sk-xxx bash install.sh --profile openai --repo-dir . --yes # China mirror bash install.sh --profile openai --mirror china EOF } # ── Argument parsing ────────────────────────────────────────────────────────── parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --profile) PROFILE="${2:-}" [[ -z "${PROFILE}" ]] && die "--profile requires a value" shift 2 ;; --install-dir) INSTALL_DIR="${2:-}" [[ -z "${INSTALL_DIR}" ]] && die "--install-dir requires a value" shift 2 ;; --version) VERSION="${2:-}" [[ -z "${VERSION}" ]] && die "--version requires a value" shift 2 ;; --repo-dir) REPO_DIR="${2:-}" [[ -z "${REPO_DIR}" ]] && die "--repo-dir requires a value" shift 2 ;; --mirror) MIRROR="${2:-}" [[ -z "${MIRROR}" ]] && die "--mirror requires a value" shift 2 ;; --yes) YES="true" shift ;; --start) START_AFTER_INSTALL="true" shift ;; -h|--help) usage exit 0 ;; *) die "Unknown argument: $1. Run with --help for usage." ;; esac done } # ── Steps ───────────────────────────────────────────────────────────────────── step_check_platform() { local os arch os="$(uname -s)" arch="$(uname -m)" case "${os}" in Darwin|Linux) ;; *) die "Unsupported OS: ${os}. This installer supports macOS and Linux." ;; esac info "Platform: ${os} ${arch}" } step_ensure_base_commands() { require_cmd curl require_cmd git } step_choose_profile() { if [[ -n "${PROFILE}" ]]; then return fi if [[ "${YES}" == "true" ]]; then PROFILE="openai" info "Non-interactive mode: defaulting to profile 'openai'" return fi printf '\n' printf '%b' "${COLOR_CYAN}Select a deployment profile:${COLOR_RESET}\n" printf ' 1) openai — OpenAI API proxy (recommended)\n' printf ' 2) kimi — Kimi 2.5 via Moonshot API\n' printf ' 3) minimax — MiniMax OpenAI-compatible API\n' printf ' q) quit\n' printf '\n' local choice prompt_input "Enter choice [1]: " choice choice="${choice:-1}" case "${choice}" in 1|openai) PROFILE="openai" ;; 2|kimi) PROFILE="kimi" ;; 3|minimax) PROFILE="minimax" ;; q|Q) info "Installation cancelled."; exit 0 ;; *) die "Invalid choice: ${choice}" ;; esac } step_ensure_uv() { if command -v uv >/dev/null 2>&1; then success "uv found: $(uv --version 2>/dev/null || echo 'unknown')" return fi info "uv not found. Installing..." curl -LsSf https://astral.sh/uv/install.sh | sh # uv installer puts the binary in ~/.local/bin or ~/.cargo/bin. # Try to pick it up without requiring the user to reload their shell. for candidate in \ "${HOME}/.local/bin" \ "${HOME}/.cargo/bin" do if [[ -x "${candidate}/uv" ]]; then export PATH="${candidate}:${PATH}" break fi done if ! command -v uv >/dev/null 2>&1; then die "uv was installed but cannot be found on PATH. Please open a new terminal and re-run." fi success "uv installed: $(uv --version 2>/dev/null || echo 'unknown')" } step_apply_mirror() { if [[ "${MIRROR}" == "china" ]]; then export UV_INDEX_URL="https://pypi.tuna.tsinghua.edu.cn/simple" info "China mirror enabled: UV_INDEX_URL=${UV_INDEX_URL}" fi } step_prepare_dirs() { mkdir -p "${INSTALL_DIR}/configs" } resolve_repo_dir() { if [[ -z "${REPO_DIR}" ]]; then REPO_DIR="${INSTALL_DIR}/DB-GPT" return fi USE_EXISTING_REPO="true" [[ -d "${REPO_DIR}" ]] || die "--repo-dir does not exist: ${REPO_DIR}" local resolved_repo_dir resolved_repo_dir="$(git -C "${REPO_DIR}" rev-parse --show-toplevel 2>/dev/null)" \ || die "--repo-dir must point to a DB-GPT git checkout: ${REPO_DIR}" REPO_DIR="${resolved_repo_dir}" if [[ "${VERSION}" != "main" ]]; then warn "--version is ignored when --repo-dir is set. Using existing checkout at ${REPO_DIR}." fi } step_clone_or_update_repo() { local repo_dir="${REPO_DIR}" if [[ "${USE_EXISTING_REPO}" == "true" ]]; then info "Using existing local repo: ${repo_dir}" return fi if [[ ! -e "${repo_dir}" ]]; then run "Cloning DB-GPT repository..." \ git clone --depth 1 --branch "${VERSION}" \ https://github.com/eosphoros-ai/DB-GPT.git "${repo_dir}" return fi if [[ ! -d "${repo_dir}/.git" ]]; then die "Directory exists but is not a git repo: ${repo_dir}. Remove it and re-run." fi info "Existing DB-GPT repo found: ${repo_dir}" if confirm "Update to version '${VERSION}'?"; then run "Fetching latest..." git -C "${repo_dir}" fetch --tags --prune run "Checking out ${VERSION}..." git -C "${repo_dir}" checkout "${VERSION}" else info "Skipping repo update." fi } step_install_deps() { local repo_dir="${REPO_DIR}" local extras extras="$(profile_extras "${PROFILE}")" info "Installing dependencies for profile: ${PROFILE}" local extra_args=() while IFS= read -r extra; do [[ -z "${extra}" ]] && continue extra_args+=(--extra "${extra}") done <<< "${extras}" ( cd "${repo_dir}" run "Running uv sync (this may take a few minutes)..." \ uv sync --all-packages "${extra_args[@]}" ) success "Dependencies installed." } step_generate_config() { local template_name template_name="$(profile_template "${PROFILE}")" local template_path template_path="$(_resolve_template "${template_name}")" local target_path="${INSTALL_DIR}/configs/${template_name}" if [[ -f "${target_path}" ]]; then if ! confirm "Config already exists: ${target_path}. Overwrite?"; then info "Keeping existing config." return fi fi cp "${template_path}" "${target_path}" # Inject API key from environment if available local api_key_env api_key_token api_key_env="$(profile_api_key_env "${PROFILE}")" api_key_token="$(profile_api_key_token "${PROFILE}")" if [[ -n "${api_key_env}" && -n "${api_key_token}" ]]; then local api_key_value="${!api_key_env:-}" if [[ -n "${api_key_value}" ]]; then replace_token "${target_path}" "${api_key_token}" "${api_key_value}" success "API key injected from \$${api_key_env}." else warn "\$${api_key_env} is not set. Please edit ${target_path} and fill in your API key before starting." fi fi } step_validate() { local repo_dir="${REPO_DIR}" ( cd "${repo_dir}" if uv run dbgpt --version >/dev/null 2>&1; then success "dbgpt CLI verified: $(uv run dbgpt --version 2>/dev/null || echo 'ok')" else warn "Could not verify dbgpt CLI. This may be fine — try starting the server." fi ) } step_print_summary() { local repo_dir="${REPO_DIR}" local template_name template_name="$(profile_template "${PROFILE}")" local config_path="${INSTALL_DIR}/configs/${template_name}" printf '%b' " ${COLOR_GREEN}════════════════════════════════════════════════════════════${COLOR_RESET} ${COLOR_GREEN} DB-GPT installed successfully!${COLOR_RESET} ${COLOR_GREEN}════════════════════════════════════════════════════════════${COLOR_RESET} Profile: ${PROFILE} Repository: ${repo_dir} Config: ${config_path} ${COLOR_CYAN}Next steps:${COLOR_RESET} 1. Review / edit your config (set API key if not done): ${COLOR_YELLOW}${config_path}${COLOR_RESET} 2. Start DB-GPT: ${COLOR_YELLOW}cd \"${repo_dir}\" && uv run dbgpt start webserver --config \"${config_path}\"${COLOR_RESET} 3. Open your browser: ${COLOR_YELLOW}http://localhost:5670${COLOR_RESET} " } step_start_if_requested() { if [[ "${START_AFTER_INSTALL}" != "true" ]]; then return fi local repo_dir="${REPO_DIR}" local template_name template_name="$(profile_template "${PROFILE}")" local config_path="${INSTALL_DIR}/configs/${template_name}" info "Starting DB-GPT server..." ( cd "${repo_dir}" exec uv run dbgpt start webserver --config "${config_path}" ) } # ── Main ────────────────────────────────────────────────────────────────────── main() { printf '%b' "${COLOR_GREEN}" cat <<'BANNER' ____ ____ ____ ____ _____ | _ \| __ ) / ___| _ \_ _| | | | | _ \ ____| | _| |_) || | | |_| | |_) |____| |_| | __/ | | |____/|____/ \____|_| |_| Quick Installer BANNER printf '%b' "${COLOR_RESET}" parse_args "$@" step_check_platform step_ensure_base_commands step_choose_profile validate_profile "${PROFILE}" step_apply_mirror step_prepare_dirs resolve_repo_dir step_ensure_uv step_clone_or_update_repo step_install_deps step_generate_config step_validate step_print_summary step_start_if_requested } main "$@"