diff --git a/README.md b/README.md index 7cb4ab4..3000187 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,18 @@ DeepSQL Desktop first ship + Agent/Brain/Editor hardening. Notes: ## Quick start +### One-liner install + +```bash +curl -fsSL https://raw.githubusercontent.com/DeepSQLAI/deepsql/main/scripts/self-host/remote-install.sh | bash +``` + +This checks prerequisites, clones the latest release to `~/deepsql`, and guides you +through configuration. See [`scripts/self-host/remote-install.sh`](scripts/self-host/remote-install.sh) +for options (`--yes` for noninteractive, `--branch` for a specific version). + +### Step-by-step + Five steps, in order. Budget about fifteen minutes, most of it waiting on the first build. ### 1. Check you have what you need diff --git a/scripts/self-host/remote-install.sh b/scripts/self-host/remote-install.sh new file mode 100755 index 0000000..31094c7 --- /dev/null +++ b/scripts/self-host/remote-install.sh @@ -0,0 +1,471 @@ +#!/usr/bin/env bash +# ═══════════════════════════════════════════════════════════════════════════════ +# DeepSQL Remote Installer +# ═══════════════════════════════════════════════════════════════════════════════ +# +# One-liner install: +# curl -fsSL https://raw.githubusercontent.com/DeepSQLAI/deepsql/main/scripts/self-host/remote-install.sh | bash +# +# This script is a convenience wrapper. The source of truth is the public GitHub +# repository: https://github.com/DeepSQLAI/deepsql +# +# You can always clone and build manually instead: +# git clone https://github.com/DeepSQLAI/deepsql.git +# cd deepsql && ./scripts/self-host/install.sh +# +# Security: This script downloads only from GitHub (raw.githubusercontent.com, +# github.com, api.github.com) for the DeepSQLAI organization. No third-party +# binaries are fetched. DeepSQL has no container registry — everything is built +# from source in your checkout. +# +# License: Apache-2.0 — https://github.com/DeepSQLAI/deepsql/blob/main/LICENSE +# ═══════════════════════════════════════════════════════════════════════════════ + +set -euo pipefail + +DEEPSQL_REPO="DeepSQLAI/deepsql" +DEEPSQL_HOME="${DEEPSQL_HOME:-$HOME/deepsql}" +DEEPSQL_BRANCH="${DEEPSQL_BRANCH:-}" # empty = auto-detect latest release tag + +# Colors (disabled if not a terminal) +if [[ -t 1 ]]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + YELLOW='\033[1;33m' + BLUE='\033[0;34m' + BOLD='\033[1m' + NC='\033[0m' +else + RED='' GREEN='' YELLOW='' BLUE='' BOLD='' NC='' +fi + +info() { printf "${BLUE}==>${NC} %s\n" "$*"; } +warn() { printf "${YELLOW}Warning:${NC} %s\n" "$*" >&2; } +error() { printf "${RED}Error:${NC} %s\n" "$*" >&2; } +success() { printf "${GREEN}✓${NC} %s\n" "$*"; } + +# ── OS / Architecture Detection ─────────────────────────────────────────────── + +detect_platform() { + OS="$(uname -s)" + ARCH="$(uname -m)" + + case "$OS" in + Linux) OS_TYPE="linux" ;; + Darwin) OS_TYPE="darwin" ;; + MINGW*|MSYS*|CYGWIN*) OS_TYPE="windows" ;; + *) + error "Unsupported operating system: $OS" + error "DeepSQL self-hosting requires Linux or macOS with Docker." + exit 1 + ;; + esac + + case "$ARCH" in + x86_64|amd64) ARCH_TYPE="amd64" ;; + aarch64|arm64) ARCH_TYPE="arm64" ;; + *) + error "Unsupported architecture: $ARCH" + exit 1 + ;; + esac + + info "Detected: $OS_TYPE/$ARCH_TYPE" +} + +# ── Prerequisite Checks ─────────────────────────────────────────────────────── + +require_command() { + local cmd="$1" + local install_hint="${2:-}" + if ! command -v "$cmd" >/dev/null 2>&1; then + error "Required command not found: $cmd" + [[ -n "$install_hint" ]] && echo " $install_hint" >&2 + return 1 + fi +} + +check_prerequisites() { + info "Checking prerequisites..." + local missing=0 + + require_command bash || missing=1 + require_command curl "Install with your package manager (apt/dnf/brew)" || missing=1 + require_command git "Install with your package manager (apt/dnf/brew)" || missing=1 + + if [[ "$missing" -eq 1 ]]; then + error "Missing required tools. Install them and retry." + exit 1 + fi + success "bash, curl, git available" +} + +version_ge() { + [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -1)" == "$2" ]] +} + +check_docker() { + info "Checking Docker..." + + if ! command -v docker >/dev/null 2>&1; then + error "Docker is not installed." + echo + if [[ "$OS_TYPE" == "linux" ]]; then + echo "On a fresh Linux server, run the bootstrap script first:" + echo " ${BOLD}curl -fsSL https://raw.githubusercontent.com/${DEEPSQL_REPO}/main/scripts/self-host/bootstrap-server.sh | sudo bash${NC}" + echo + echo "Or install Docker manually:" + echo " curl -fsSL https://get.docker.com | sh" + echo + echo "After installing Docker, re-run this installer:" + echo " curl -fsSL https://raw.githubusercontent.com/${DEEPSQL_REPO}/main/scripts/self-host/remote-install.sh | bash" + elif [[ "$OS_TYPE" == "darwin" ]]; then + echo "Install Docker Desktop for Mac from:" + echo " https://www.docker.com/products/docker-desktop" + fi + exit 1 + fi + + if ! docker info >/dev/null 2>&1; then + error "Docker daemon is not running or you lack permission." + echo + echo "If Docker is installed but not running:" + echo " sudo systemctl start docker" + echo + echo "If you need permission:" + echo " sudo usermod -aG docker \$USER" + echo " # Then log out and back in" + exit 1 + fi + success "Docker is running" + + # Check Compose v2 + local compose_version + compose_version="$(docker compose version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)" + if [[ -z "$compose_version" ]]; then + error "Docker Compose v2 is not installed." + echo + echo "Compose v2 comes bundled with Docker Desktop. On Linux, install the plugin:" + echo " apt install docker-compose-plugin # Debian/Ubuntu" + echo " dnf install docker-compose-plugin # RHEL/Fedora" + exit 1 + fi + if ! version_ge "$compose_version" "2.0.0"; then + error "Docker Compose $compose_version is too old (need >= 2.0.0)." + exit 1 + fi + success "Docker Compose $compose_version" + + # Check buildx (required for multi-stage builds) + local buildx_version + buildx_version="$(docker buildx version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)" + if [[ -z "$buildx_version" ]]; then + error "Docker buildx is not installed." + echo + echo "buildx comes bundled with Docker Desktop. On Linux:" + echo " apt install docker-buildx-plugin # Debian/Ubuntu" + echo + echo "Or run the bootstrap script:" + echo " curl -fsSL https://raw.githubusercontent.com/${DEEPSQL_REPO}/main/scripts/self-host/bootstrap-server.sh | sudo bash" + echo + echo "Then re-run this installer." + exit 1 + fi + if ! version_ge "$buildx_version" "0.17.0"; then + error "Docker buildx $buildx_version is too old (need >= 0.17.0)." + echo + echo "Upgrade buildx or run the bootstrap script to install a newer version." + exit 1 + fi + success "Docker buildx $buildx_version" +} + +# ── Release Tag Detection ───────────────────────────────────────────────────── + +get_latest_release_tag() { + # Query GitHub API for tags, filter to product tags (v[0-9]*), exclude desktop-v* tags + local tags + tags="$(curl -fsSL "https://api.github.com/repos/${DEEPSQL_REPO}/tags?per_page=50" 2>/dev/null || true)" + + if [[ -z "$tags" ]]; then + return 1 + fi + + # Extract tag names, filter to product releases only: + # - Must start with v followed by a digit (v1.0.0, v2.3.4, etc.) + # - Excludes desktop-v*, agent-v*, or any other prefixed tags + local latest + latest="$(echo "$tags" \ + | grep -o '"name": *"[^"]*"' \ + | cut -d'"' -f4 \ + | grep -E '^v[0-9]' \ + | grep -v '^desktop-' \ + | head -1 || true)" + + if [[ -n "$latest" ]]; then + echo "$latest" + return 0 + fi + + return 1 +} + +# ── Clone / Update Repository ───────────────────────────────────────────────── + +clone_or_update() { + local target_ref="$1" + + if [[ -d "$DEEPSQL_HOME/.git" ]]; then + info "Updating existing checkout at $DEEPSQL_HOME..." + cd "$DEEPSQL_HOME" + + # Fetch latest (include tags) + if ! git fetch --tags origin 2>/dev/null; then + warn "Failed to fetch updates. Continuing with existing checkout." + fi + + if [[ -n "$target_ref" ]]; then + info "Checking out $target_ref..." + git checkout "$target_ref" 2>/dev/null || git checkout -b "$target_ref" "origin/$target_ref" 2>/dev/null || { + # If it's a tag, just checkout directly + git checkout "$target_ref" 2>/dev/null || { + warn "Could not checkout $target_ref. Staying on current branch." + } + } + fi + + success "Repository updated" + report_version "$target_ref" + else + info "Cloning DeepSQL to $DEEPSQL_HOME..." + + mkdir -p "$(dirname "$DEEPSQL_HOME")" + + local clone_args=(--depth 1) + if [[ -n "$target_ref" ]]; then + clone_args+=(--branch "$target_ref") + fi + + if ! git clone "${clone_args[@]}" "https://github.com/${DEEPSQL_REPO}.git" "$DEEPSQL_HOME"; then + error "Failed to clone repository." + exit 1 + fi + + cd "$DEEPSQL_HOME" + success "Repository cloned" + report_version "$target_ref" + fi +} + +report_version() { + local target_ref="$1" + + # For shallow clones, git describe may not work correctly, so prefer the + # target ref we requested if it looks like a version tag + if [[ "$target_ref" =~ ^v[0-9] ]]; then + success "Version: $target_ref" + return + fi + + # Try to get the current tag or branch + local current_tag current_branch + current_tag="$(git describe --tags --exact-match 2>/dev/null || true)" + current_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)" + + if [[ -n "$current_tag" ]]; then + success "Version: $current_tag" + elif [[ -n "$target_ref" ]]; then + success "Branch: $target_ref" + elif [[ -n "$current_branch" && "$current_branch" != "HEAD" ]]; then + success "Branch: $current_branch" + else + success "Checked out at $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" + fi +} + +# ── Environment Setup ───────────────────────────────────────────────────────── + +setup_env() { + cd "$DEEPSQL_HOME" + + if [[ ! -f ".env" ]]; then + if [[ -f ".env.example" ]]; then + info "Creating .env from template..." + cp .env.example .env + success "Created .env" + else + error ".env.example not found in checkout." + exit 1 + fi + else + info ".env already exists — not overwritten" + fi +} + +# ── Print Next Steps ────────────────────────────────────────────────────────── + +print_llm_setup() { + echo + echo "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "${BOLD} IMPORTANT: Configure your LLM before running install.sh${NC}" + echo "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo + echo "DeepSQL requires you to bring your own LLM. Edit ${BOLD}$DEEPSQL_HOME/.env${NC}" + echo "and set these variables:" + echo + echo " ${GREEN}DEEPSQL_CHAT_PROVIDER${NC}=openai" + echo " ${GREEN}DEEPSQL_CHAT_API_KEY${NC}=sk-your-key" + echo " ${GREEN}DEEPSQL_CHAT_ENDPOINT${NC}=https://api.openai.com/v1" + echo " ${GREEN}DEEPSQL_CHAT_MODEL${NC}=gpt-4o" + echo + echo "For Azure OpenAI, Anthropic, Ollama, or other providers, see the" + echo "examples in .env.example or the README." + echo +} + +print_final_instructions() { + local port="${DEEPSQL_FRONTEND_PORT:-3000}" + + echo + echo "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo "${GREEN}${BOLD} DeepSQL is ready to install!${NC}" + echo "${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo + echo "Next steps:" + echo + echo " 1. ${BOLD}Edit .env${NC} with your LLM credentials (see above)" + echo + echo " 2. ${BOLD}Run the installer:${NC}" + echo " cd $DEEPSQL_HOME" + echo " ./scripts/self-host/install.sh" + echo + echo " 3. ${BOLD}Open DeepSQL:${NC}" + echo " http://localhost:$port" + echo + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo + echo "Resources:" + echo " Documentation: https://github.com/${DEEPSQL_REPO}#readme" + echo " Whitepaper: https://deepsql.ai/whitepaper" + echo " Issues: https://github.com/${DEEPSQL_REPO}/issues" + echo +} + +# ── Run Install Script (optional) ───────────────────────────────────────────── + +run_install() { + cd "$DEEPSQL_HOME" + + if [[ ! -x "./scripts/self-host/install.sh" ]]; then + error "install.sh not found or not executable." + exit 1 + fi + + info "Running install.sh..." + echo + + # Pass through any arguments to install.sh + exec ./scripts/self-host/install.sh "$@" +} + +# ── Main ────────────────────────────────────────────────────────────────────── + +usage() { + cat < Use a specific branch or tag instead of latest release + +Environment variables: + DEEPSQL_HOME Installation directory (default: \$HOME/deepsql) + DEEPSQL_BRANCH Branch or tag to checkout (default: latest release) + +Examples: + # Interactive setup (edit .env, then run install.sh yourself) + curl -fsSL https://raw.githubusercontent.com/DeepSQLAI/deepsql/main/scripts/self-host/remote-install.sh | bash + + # Noninteractive (requires .env to be pre-configured or prompts) + curl -fsSL .../remote-install.sh | bash -s -- --yes + + # Install a specific version + curl -fsSL .../remote-install.sh | bash -s -- --branch v1.3.0 +EOF +} + +main() { + local auto_install=0 + local skip_docker=0 + + while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + usage + exit 0 + ;; + -y|--yes) + auto_install=1 + shift + ;; + --skip-docker-check) + skip_docker=1 + shift + ;; + --branch) + DEEPSQL_BRANCH="$2" + shift 2 + ;; + *) + error "Unknown option: $1" + usage + exit 1 + ;; + esac + done + + echo + echo "${BOLD}═══════════════════════════════════════════════════════════════════════════${NC}" + echo "${BOLD} DeepSQL Remote Installer${NC}" + echo "${BOLD}═══════════════════════════════════════════════════════════════════════════${NC}" + echo + + detect_platform + check_prerequisites + + if [[ "$skip_docker" -eq 0 ]]; then + check_docker + else + warn "Skipping Docker check (--skip-docker-check)" + fi + + # Determine target ref + local target_ref="$DEEPSQL_BRANCH" + if [[ -z "$target_ref" ]]; then + info "Finding latest stable release..." + target_ref="$(get_latest_release_tag || true)" + if [[ -z "$target_ref" ]]; then + target_ref="main" + warn "Could not determine latest release. Using default branch: $target_ref" + else + success "Latest release: $target_ref" + fi + fi + + clone_or_update "$target_ref" + setup_env + + if [[ "$auto_install" -eq 1 ]]; then + print_llm_setup + run_install + else + print_llm_setup + print_final_instructions + fi +} + +main "$@"