- Add one-line installer (scripts/install.sh) with platform detection - Add doctor command (scripts/commands/doctor.sh) for environment diagnostics - Add shared libraries: dependencies, docker, platform, validation - Update README with quick-start installer instructions - Add AGENTS.md with codebase patterns for AI agent context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
909 lines
25 KiB
Bash
909 lines
25 KiB
Bash
#!/bin/bash
|
|
# Dependency management functions for Mosaic Stack installer
|
|
# Handles installation and verification of all required dependencies
|
|
|
|
# shellcheck source=lib/platform.sh
|
|
source "${BASH_SOURCE[0]%/*}/platform.sh"
|
|
|
|
# ============================================================================
|
|
# Dependency Version Requirements
|
|
# ============================================================================
|
|
|
|
MIN_NODE_VERSION=22
|
|
MIN_DOCKER_VERSION=24
|
|
MIN_PNPM_VERSION=10
|
|
MIN_POSTGRES_VERSION=17
|
|
|
|
# ============================================================================
|
|
# Generic Command Checking
|
|
# ============================================================================
|
|
|
|
# Check if a command exists
|
|
check_command() {
|
|
command -v "$1" &>/dev/null
|
|
}
|
|
|
|
# Get version of a command (generic)
|
|
get_command_version() {
|
|
local cmd="$1"
|
|
local flag="${2:---version}"
|
|
|
|
"$cmd" "$flag" 2>/dev/null | head -1
|
|
}
|
|
|
|
# Extract major version number from version string
|
|
extract_major_version() {
|
|
local version="$1"
|
|
echo "$version" | grep -oE '[0-9]+' | head -1
|
|
}
|
|
|
|
# ============================================================================
|
|
# Git
|
|
# ============================================================================
|
|
|
|
check_git() {
|
|
if check_command git; then
|
|
local version
|
|
version=$(git --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
echo -e "${SUCCESS}✓${NC} Git: ${INFO}$version${NC}"
|
|
return 0
|
|
fi
|
|
echo -e "${WARN}→${NC} Git not found"
|
|
return 1
|
|
}
|
|
|
|
install_git() {
|
|
local os pkg
|
|
os=$(detect_os)
|
|
pkg=$(detect_package_manager "$os")
|
|
|
|
echo -e "${WARN}→${NC} Installing Git..."
|
|
|
|
case "$pkg" in
|
|
brew)
|
|
brew install git
|
|
;;
|
|
apt)
|
|
maybe_sudo apt-get update -y
|
|
maybe_sudo apt-get install -y git
|
|
;;
|
|
pacman)
|
|
maybe_sudo pacman -Sy --noconfirm git
|
|
;;
|
|
dnf)
|
|
maybe_sudo dnf install -y git
|
|
;;
|
|
yum)
|
|
maybe_sudo yum install -y git
|
|
;;
|
|
*)
|
|
echo -e "${ERROR}Error: Unknown package manager for Git installation${NC}"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
echo -e "${SUCCESS}✓${NC} Git installed"
|
|
}
|
|
|
|
ensure_git() {
|
|
if ! check_git; then
|
|
install_git
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Homebrew (macOS)
|
|
# ============================================================================
|
|
|
|
check_homebrew() {
|
|
if check_command brew; then
|
|
local prefix
|
|
prefix=$(brew --prefix 2>/dev/null)
|
|
echo -e "${SUCCESS}✓${NC} Homebrew: ${INFO}$prefix${NC}"
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
install_homebrew() {
|
|
echo -e "${WARN}→${NC} Installing Homebrew..."
|
|
|
|
# Download and run the Homebrew installer
|
|
local tmp
|
|
tmp=$(create_temp_file)
|
|
|
|
if ! download_file "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh" "$tmp"; then
|
|
echo -e "${ERROR}Error: Failed to download Homebrew installer${NC}"
|
|
return 1
|
|
fi
|
|
|
|
NONINTERACTIVE=1 /bin/bash "$tmp"
|
|
local ret=$?
|
|
rm -f "$tmp"
|
|
|
|
if [[ $ret -ne 0 ]]; then
|
|
echo -e "${ERROR}Error: Homebrew installation failed${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Add Homebrew to PATH for this session
|
|
if [[ -f "/opt/homebrew/bin/brew" ]]; then
|
|
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
elif [[ -f "/usr/local/bin/brew" ]]; then
|
|
eval "$(/usr/local/bin/brew shellenv)"
|
|
fi
|
|
|
|
echo -e "${SUCCESS}✓${NC} Homebrew installed"
|
|
}
|
|
|
|
ensure_homebrew() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
if [[ "$os" != "macos" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
if ! check_homebrew; then
|
|
install_homebrew
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# Node.js
|
|
# ============================================================================
|
|
|
|
check_node() {
|
|
local min_version="${1:-$MIN_NODE_VERSION}"
|
|
|
|
if ! check_command node; then
|
|
echo -e "${WARN}→${NC} Node.js not found"
|
|
return 1
|
|
fi
|
|
|
|
local version
|
|
version=$(node --version 2>/dev/null | sed 's/v//')
|
|
local major
|
|
major=$(extract_major_version "$version")
|
|
|
|
if [[ "$major" -ge "$min_version" ]]; then
|
|
echo -e "${SUCCESS}✓${NC} Node.js: ${INFO}v$version${NC}"
|
|
return 0
|
|
else
|
|
echo -e "${WARN}→${NC} Node.js v$version found, but v${min_version}+ required"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
install_node_macos() {
|
|
echo -e "${WARN}→${NC} Installing Node.js via Homebrew..."
|
|
|
|
ensure_homebrew
|
|
|
|
# Install node@22
|
|
brew install node@22
|
|
|
|
# Link it
|
|
brew link node@22 --overwrite --force 2>/dev/null || true
|
|
|
|
# Ensure it's on PATH
|
|
local prefix
|
|
prefix=$(brew --prefix node@22 2>/dev/null)
|
|
if [[ -n "$prefix" && -d "${prefix}/bin" ]]; then
|
|
export PATH="${prefix}/bin:$PATH"
|
|
fi
|
|
|
|
echo -e "${SUCCESS}✓${NC} Node.js installed"
|
|
}
|
|
|
|
install_node_debian() {
|
|
echo -e "${WARN}→${NC} Installing Node.js via NodeSource..."
|
|
|
|
require_sudo
|
|
|
|
local tmp
|
|
tmp=$(create_temp_file)
|
|
|
|
# Download NodeSource setup script for Node.js 22
|
|
if ! download_file "https://deb.nodesource.com/setup_22.x" "$tmp"; then
|
|
echo -e "${ERROR}Error: Failed to download NodeSource setup script${NC}"
|
|
return 1
|
|
fi
|
|
|
|
maybe_sudo -E bash "$tmp"
|
|
maybe_sudo apt-get install -y nodejs
|
|
|
|
rm -f "$tmp"
|
|
echo -e "${SUCCESS}✓${NC} Node.js installed"
|
|
}
|
|
|
|
install_node_fedora() {
|
|
echo -e "${WARN}→${NC} Installing Node.js via NodeSource..."
|
|
|
|
require_sudo
|
|
|
|
local tmp
|
|
tmp=$(create_temp_file)
|
|
|
|
if ! download_file "https://rpm.nodesource.com/setup_22.x" "$tmp"; then
|
|
echo -e "${ERROR}Error: Failed to download NodeSource setup script${NC}"
|
|
return 1
|
|
fi
|
|
|
|
maybe_sudo bash "$tmp"
|
|
|
|
if command -v dnf &>/dev/null; then
|
|
maybe_sudo dnf install -y nodejs
|
|
else
|
|
maybe_sudo yum install -y nodejs
|
|
fi
|
|
|
|
rm -f "$tmp"
|
|
echo -e "${SUCCESS}✓${NC} Node.js installed"
|
|
}
|
|
|
|
install_node_arch() {
|
|
echo -e "${WARN}→${NC} Installing Node.js via pacman..."
|
|
|
|
maybe_sudo pacman -Sy --noconfirm nodejs npm
|
|
echo -e "${SUCCESS}✓${NC} Node.js installed"
|
|
}
|
|
|
|
install_node() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
case "$os" in
|
|
macos)
|
|
install_node_macos
|
|
;;
|
|
debian)
|
|
install_node_debian
|
|
;;
|
|
arch)
|
|
install_node_arch
|
|
;;
|
|
fedora)
|
|
install_node_fedora
|
|
;;
|
|
*)
|
|
echo -e "${ERROR}Error: Unsupported OS for Node.js installation: $os${NC}"
|
|
echo "Please install Node.js ${MIN_NODE_VERSION}+ manually: https://nodejs.org"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
ensure_node() {
|
|
if ! check_node; then
|
|
install_node
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# pnpm
|
|
# ============================================================================
|
|
|
|
check_pnpm() {
|
|
if check_command pnpm; then
|
|
local version
|
|
version=$(pnpm --version 2>/dev/null)
|
|
local major
|
|
major=$(extract_major_version "$version")
|
|
|
|
if [[ "$major" -ge "$MIN_PNPM_VERSION" ]]; then
|
|
echo -e "${SUCCESS}✓${NC} pnpm: ${INFO}v$version${NC}"
|
|
return 0
|
|
else
|
|
echo -e "${WARN}→${NC} pnpm v$version found, but v${MIN_PNPM_VERSION}+ recommended"
|
|
return 1
|
|
fi
|
|
fi
|
|
echo -e "${WARN}→${NC} pnpm not found"
|
|
return 1
|
|
}
|
|
|
|
install_pnpm() {
|
|
# Try corepack first (comes with Node.js 22+)
|
|
if check_command corepack; then
|
|
echo -e "${WARN}→${NC} Installing pnpm via Corepack..."
|
|
corepack enable 2>/dev/null || true
|
|
corepack prepare pnpm@${MIN_PNPM_VERSION} --activate
|
|
echo -e "${SUCCESS}✓${NC} pnpm installed via Corepack"
|
|
return 0
|
|
fi
|
|
|
|
# Fall back to npm
|
|
echo -e "${WARN}→${NC} Installing pnpm via npm..."
|
|
|
|
# Fix npm permissions on Linux first
|
|
local os
|
|
os=$(detect_os)
|
|
if [[ "$os" != "macos" ]]; then
|
|
fix_npm_permissions
|
|
fi
|
|
|
|
npm install -g pnpm@${MIN_PNPM_VERSION}
|
|
echo -e "${SUCCESS}✓${NC} pnpm installed via npm"
|
|
}
|
|
|
|
ensure_pnpm() {
|
|
if ! check_pnpm; then
|
|
install_pnpm
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# npm Permissions (Linux)
|
|
# ============================================================================
|
|
|
|
fix_npm_permissions() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
if [[ "$os" == "macos" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local npm_prefix
|
|
npm_prefix=$(npm config get prefix 2>/dev/null || true)
|
|
|
|
if [[ -z "$npm_prefix" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Check if we can write to the npm prefix
|
|
if [[ -w "$npm_prefix" || -w "${npm_prefix}/lib" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${WARN}→${NC} Configuring npm for user-local installs..."
|
|
|
|
# Create user-local npm directory
|
|
mkdir -p "$HOME/.npm-global"
|
|
|
|
# Configure npm to use it
|
|
npm config set prefix "$HOME/.npm-global"
|
|
|
|
# Add to shell config
|
|
local rc
|
|
for rc in "$HOME/.bashrc" "$HOME/.zshrc"; do
|
|
if [[ -f "$rc" ]]; then
|
|
# shellcheck disable=SC2016
|
|
if ! grep -q ".npm-global" "$rc" 2>/dev/null; then
|
|
echo "" >> "$rc"
|
|
echo "# Added by Mosaic Stack installer" >> "$rc"
|
|
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> "$rc"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Update PATH for current session
|
|
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
|
|
echo -e "${SUCCESS}✓${NC} npm configured for user installs"
|
|
}
|
|
|
|
# Get npm global bin directory
|
|
npm_global_bin_dir() {
|
|
local prefix
|
|
prefix=$(npm prefix -g 2>/dev/null || true)
|
|
|
|
if [[ -n "$prefix" && "$prefix" == /* ]]; then
|
|
echo "${prefix%/}/bin"
|
|
return 0
|
|
fi
|
|
|
|
prefix=$(npm config get prefix 2>/dev/null || true)
|
|
if [[ -n "$prefix" && "$prefix" != "undefined" && "$prefix" != "null" && "$prefix" == /* ]]; then
|
|
echo "${prefix%/}/bin"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# ============================================================================
|
|
# Docker
|
|
# ============================================================================
|
|
|
|
check_docker() {
|
|
if ! check_command docker; then
|
|
echo -e "${WARN}→${NC} Docker not found"
|
|
return 1
|
|
fi
|
|
|
|
# Check if daemon is accessible
|
|
if ! docker info &>/dev/null; then
|
|
local error_msg
|
|
error_msg=$(docker info 2>&1)
|
|
|
|
if [[ "$error_msg" =~ "permission denied" ]]; then
|
|
echo -e "${WARN}→${NC} Docker installed but permission denied"
|
|
echo -e " ${INFO}Fix: sudo usermod -aG docker \$USER${NC}"
|
|
echo -e " Then log out and back in"
|
|
return 2
|
|
elif [[ "$error_msg" =~ "Cannot connect to the Docker daemon" ]]; then
|
|
echo -e "${WARN}→${NC} Docker installed but daemon not running"
|
|
return 3
|
|
else
|
|
echo -e "${WARN}→${NC} Docker installed but not accessible"
|
|
return 4
|
|
fi
|
|
fi
|
|
|
|
local version
|
|
version=$(docker --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
local major
|
|
major=$(extract_major_version "$version")
|
|
|
|
if [[ "$major" -ge "$MIN_DOCKER_VERSION" ]]; then
|
|
echo -e "${SUCCESS}✓${NC} Docker: ${INFO}$version${NC}"
|
|
return 0
|
|
else
|
|
echo -e "${WARN}→${NC} Docker v$version found, but v${MIN_DOCKER_VERSION}+ recommended"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
check_docker_compose() {
|
|
# Check for docker compose plugin first
|
|
if docker compose version &>/dev/null; then
|
|
local version
|
|
version=$(docker compose version --short 2>/dev/null || docker compose version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
echo -e "${SUCCESS}✓${NC} Docker Compose: ${INFO}$version (plugin)${NC}"
|
|
return 0
|
|
fi
|
|
|
|
# Check for standalone docker-compose
|
|
if check_command docker-compose; then
|
|
local version
|
|
version=$(docker-compose --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
|
|
echo -e "${SUCCESS}✓${NC} Docker Compose: ${INFO}$version (standalone)${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${WARN}→${NC} Docker Compose not found"
|
|
return 1
|
|
}
|
|
|
|
install_docker_macos() {
|
|
echo -e "${WARN}→${NC} Installing Docker Desktop for macOS..."
|
|
|
|
ensure_homebrew
|
|
|
|
brew install --cask docker
|
|
|
|
echo -e "${SUCCESS}✓${NC} Docker Desktop installed"
|
|
echo -e "${INFO}i${NC} Please open Docker Desktop to complete setup"
|
|
}
|
|
|
|
install_docker_debian() {
|
|
echo -e "${WARN}→${NC} Installing Docker via docker.com apt repo..."
|
|
|
|
require_sudo
|
|
|
|
# Install dependencies
|
|
maybe_sudo apt-get update
|
|
maybe_sudo apt-get install -y ca-certificates curl gnupg
|
|
|
|
# Add Docker's official GPG key
|
|
local keyring="/usr/share/keyrings/docker-archive-keyring.gpg"
|
|
maybe_sudo install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/debian/gpg | maybe_sudo gpg --dearmor -o "$keyring"
|
|
maybe_sudo chmod a+r "$keyring"
|
|
|
|
# Add Docker repository
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=$keyring] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | \
|
|
maybe_sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
|
|
# Install Docker
|
|
maybe_sudo apt-get update
|
|
maybe_sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
|
|
# Start Docker
|
|
maybe_sudo systemctl enable --now docker
|
|
|
|
# Add user to docker group
|
|
maybe_sudo usermod -aG docker "$USER"
|
|
|
|
echo -e "${SUCCESS}✓${NC} Docker installed"
|
|
echo -e "${INFO}i${NC} Run 'newgrp docker' or log out/in for group membership"
|
|
}
|
|
|
|
install_docker_ubuntu() {
|
|
echo -e "${WARN}→${NC} Installing Docker via docker.com apt repo..."
|
|
|
|
require_sudo
|
|
|
|
# Install dependencies
|
|
maybe_sudo apt-get update
|
|
maybe_sudo apt-get install -y ca-certificates curl gnupg
|
|
|
|
# Add Docker's official GPG key
|
|
local keyring="/usr/share/keyrings/docker-archive-keyring.gpg"
|
|
maybe_sudo install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | maybe_sudo gpg --dearmor -o "$keyring"
|
|
maybe_sudo chmod a+r "$keyring"
|
|
|
|
# Add Docker repository
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=$keyring] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
|
|
maybe_sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
|
|
# Install Docker
|
|
maybe_sudo apt-get update
|
|
maybe_sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
|
|
|
# Start Docker
|
|
maybe_sudo systemctl enable --now docker
|
|
|
|
# Add user to docker group
|
|
maybe_sudo usermod -aG docker "$USER"
|
|
|
|
echo -e "${SUCCESS}✓${NC} Docker installed"
|
|
echo -e "${INFO}i${NC} Run 'newgrp docker' or log out/in for group membership"
|
|
}
|
|
|
|
install_docker_fedora() {
|
|
echo -e "${WARN}→${NC} Installing Docker via dnf..."
|
|
|
|
require_sudo
|
|
|
|
# Add Docker repository
|
|
maybe_sudo dnf -y install dnf-plugins-core
|
|
maybe_sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
|
|
|
# Install Docker
|
|
maybe_sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
|
|
# Start Docker
|
|
maybe_sudo systemctl enable --now docker
|
|
|
|
# Add user to docker group
|
|
maybe_sudo usermod -aG docker "$USER"
|
|
|
|
echo -e "${SUCCESS}✓${NC} Docker installed"
|
|
echo -e "${INFO}i${NC} Run 'newgrp docker' or log out/in for group membership"
|
|
}
|
|
|
|
install_docker_arch() {
|
|
echo -e "${WARN}→${NC} Installing Docker via pacman..."
|
|
|
|
maybe_sudo pacman -Sy --noconfirm docker docker-compose
|
|
|
|
# Start Docker
|
|
maybe_sudo systemctl enable --now docker
|
|
|
|
# Add user to docker group
|
|
maybe_sudo usermod -aG docker "$USER"
|
|
|
|
echo -e "${SUCCESS}✓${NC} Docker installed"
|
|
echo -e "${INFO}i${NC} Run 'newgrp docker' or log out/in for group membership"
|
|
}
|
|
|
|
install_docker() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
case "$os" in
|
|
macos)
|
|
install_docker_macos
|
|
;;
|
|
debian)
|
|
# Check if Ubuntu specifically
|
|
if [[ -f /etc/os-release ]]; then
|
|
source /etc/os-release
|
|
if [[ "$ID" == "ubuntu" ]]; then
|
|
install_docker_ubuntu
|
|
return $?
|
|
fi
|
|
fi
|
|
install_docker_debian
|
|
;;
|
|
arch)
|
|
install_docker_arch
|
|
;;
|
|
fedora)
|
|
install_docker_fedora
|
|
;;
|
|
*)
|
|
echo -e "${ERROR}Error: Unsupported OS for Docker installation: $os${NC}"
|
|
echo "Please install Docker manually: https://docs.docker.com/get-docker/"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
ensure_docker() {
|
|
local check_result
|
|
check_docker
|
|
check_result=$?
|
|
|
|
if [[ $check_result -eq 0 ]]; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ $check_result -eq 2 ]]; then
|
|
# Permission issue - try to fix
|
|
echo -e "${WARN}→${NC} Attempting to fix Docker permissions..."
|
|
maybe_sudo usermod -aG docker "$USER"
|
|
echo -e "${INFO}i${NC} Run 'newgrp docker' or log out/in for group membership"
|
|
return 1
|
|
fi
|
|
|
|
if [[ $check_result -eq 3 ]]; then
|
|
# Daemon not running - try to start
|
|
echo -e "${WARN}→${NC} Starting Docker daemon..."
|
|
maybe_sudo systemctl start docker
|
|
sleep 3
|
|
check_docker
|
|
return $?
|
|
fi
|
|
|
|
# Docker not installed
|
|
install_docker
|
|
}
|
|
|
|
start_docker() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
if [[ "$os" == "macos" ]]; then
|
|
if ! pgrep -x "Docker Desktop" &>/dev/null; then
|
|
echo -e "${WARN}→${NC} Starting Docker Desktop..."
|
|
open -a "Docker Desktop"
|
|
sleep 10
|
|
fi
|
|
else
|
|
if ! docker info &>/dev/null; then
|
|
echo -e "${WARN}→${NC} Starting Docker daemon..."
|
|
maybe_sudo systemctl start docker
|
|
sleep 3
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ============================================================================
|
|
# PostgreSQL
|
|
# ============================================================================
|
|
|
|
check_postgres() {
|
|
if check_command psql; then
|
|
local version
|
|
version=$(psql --version 2>/dev/null | grep -oE '[0-9]+')
|
|
echo -e "${SUCCESS}✓${NC} PostgreSQL: ${INFO}v$version${NC}"
|
|
return 0
|
|
fi
|
|
echo -e "${WARN}→${NC} PostgreSQL not found"
|
|
return 1
|
|
}
|
|
|
|
install_postgres_macos() {
|
|
echo -e "${WARN}→${NC} Installing PostgreSQL via Homebrew..."
|
|
|
|
ensure_homebrew
|
|
|
|
brew install postgresql@17
|
|
brew link postgresql@17 --overwrite --force 2>/dev/null || true
|
|
|
|
# Start PostgreSQL
|
|
brew services start postgresql@17
|
|
|
|
echo -e "${SUCCESS}✓${NC} PostgreSQL installed"
|
|
}
|
|
|
|
install_postgres_debian() {
|
|
echo -e "${WARN}→${NC} Installing PostgreSQL via apt..."
|
|
|
|
require_sudo
|
|
|
|
# Add PostgreSQL APT repository
|
|
maybe_sudo apt-get install -y curl ca-certificates
|
|
maybe_sudo install -d /usr/share/postgresql-common/pgdg
|
|
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | maybe_sudo gpg --dearmor -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg
|
|
|
|
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.gpg] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | \
|
|
maybe_sudo tee /etc/apt/sources.list.d/pgdg.list > /dev/null
|
|
|
|
maybe_sudo apt-get update
|
|
maybe_sudo apt-get install -y postgresql-17 postgresql-17-pgvector
|
|
|
|
# Start PostgreSQL
|
|
maybe_sudo systemctl enable --now postgresql
|
|
|
|
echo -e "${SUCCESS}✓${NC} PostgreSQL installed"
|
|
}
|
|
|
|
install_postgres_arch() {
|
|
echo -e "${WARN}→${NC} Installing PostgreSQL via pacman..."
|
|
|
|
maybe_sudo pacman -Sy --noconfirm postgresql
|
|
|
|
# Initialize database
|
|
maybe_sudo -u postgres initdb -D /var/lib/postgres/data 2>/dev/null || true
|
|
|
|
# Start PostgreSQL
|
|
maybe_sudo systemctl enable --now postgresql
|
|
|
|
echo -e "${SUCCESS}✓${NC} PostgreSQL installed"
|
|
}
|
|
|
|
install_postgres_fedora() {
|
|
echo -e "${WARN}→${NC} Installing PostgreSQL via dnf..."
|
|
|
|
maybe_sudo dnf install -y postgresql-server postgresql-contrib
|
|
|
|
# Initialize database
|
|
maybe_sudo postgresql-setup --initdb 2>/dev/null || true
|
|
|
|
# Start PostgreSQL
|
|
maybe_sudo systemctl enable --now postgresql
|
|
|
|
echo -e "${SUCCESS}✓${NC} PostgreSQL installed"
|
|
}
|
|
|
|
install_postgres() {
|
|
local os
|
|
os=$(detect_os)
|
|
|
|
case "$os" in
|
|
macos)
|
|
install_postgres_macos
|
|
;;
|
|
debian)
|
|
install_postgres_debian
|
|
;;
|
|
arch)
|
|
install_postgres_arch
|
|
;;
|
|
fedora)
|
|
install_postgres_fedora
|
|
;;
|
|
*)
|
|
echo -e "${ERROR}Error: Unsupported OS for PostgreSQL installation: $os${NC}"
|
|
echo "Please install PostgreSQL ${MIN_POSTGRES_VERSION}+ manually"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# Dependency Summary
|
|
# ============================================================================
|
|
|
|
# Check all dependencies for Docker mode
|
|
check_docker_dependencies() {
|
|
local errors=0
|
|
|
|
echo -e "${BOLD}Checking Docker dependencies...${NC}"
|
|
echo ""
|
|
|
|
# Git (optional but recommended)
|
|
check_git || ((errors++))
|
|
|
|
# Docker
|
|
local docker_result
|
|
check_docker
|
|
docker_result=$?
|
|
if [[ $docker_result -ne 0 ]]; then
|
|
((errors++))
|
|
fi
|
|
|
|
# Docker Compose
|
|
if [[ $docker_result -eq 0 ]]; then
|
|
check_docker_compose || ((errors++))
|
|
fi
|
|
|
|
echo ""
|
|
|
|
if [[ $errors -gt 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Check all dependencies for Native mode
|
|
check_native_dependencies() {
|
|
local errors=0
|
|
|
|
echo -e "${BOLD}Checking native dependencies...${NC}"
|
|
echo ""
|
|
|
|
check_git || ((errors++))
|
|
check_node || ((errors++))
|
|
check_pnpm || ((errors++))
|
|
check_postgres || ((errors++))
|
|
|
|
echo ""
|
|
|
|
if [[ $errors -gt 0 ]]; then
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Install missing dependencies based on mode
|
|
install_dependencies() {
|
|
local mode="$1"
|
|
|
|
echo -e "${BOLD}Installing dependencies...${NC}"
|
|
echo ""
|
|
|
|
ensure_git
|
|
|
|
if [[ "$mode" == "docker" ]]; then
|
|
ensure_docker
|
|
start_docker
|
|
else
|
|
ensure_node
|
|
ensure_pnpm
|
|
|
|
local os
|
|
os=$(detect_os)
|
|
if [[ "$os" != "macos" ]]; then
|
|
fix_npm_permissions
|
|
fi
|
|
|
|
# PostgreSQL is optional for native mode (can use Docker or external)
|
|
if ! check_postgres; then
|
|
echo -e "${INFO}i${NC} PostgreSQL not installed - you can use Docker or external database"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${SUCCESS}✓${NC} Dependencies installed"
|
|
}
|
|
|
|
# ============================================================================
|
|
# Package Name Mapping
|
|
# ============================================================================
|
|
|
|
# Get platform-specific package name
|
|
get_package_name() {
|
|
local pkg_manager="$1"
|
|
local package="$2"
|
|
|
|
case "$pkg_manager" in
|
|
apt)
|
|
case "$package" in
|
|
docker) echo "docker-ce" ;;
|
|
docker-compose) echo "docker-compose-plugin" ;;
|
|
node) echo "nodejs" ;;
|
|
postgres) echo "postgresql-17" ;;
|
|
*) echo "$package" ;;
|
|
esac
|
|
;;
|
|
pacman)
|
|
case "$package" in
|
|
docker) echo "docker" ;;
|
|
docker-compose) echo "docker-compose" ;;
|
|
node) echo "nodejs" ;;
|
|
postgres) echo "postgresql" ;;
|
|
*) echo "$package" ;;
|
|
esac
|
|
;;
|
|
dnf)
|
|
case "$package" in
|
|
docker) echo "docker-ce" ;;
|
|
docker-compose) echo "docker-compose-plugin" ;;
|
|
node) echo "nodejs" ;;
|
|
postgres) echo "postgresql-server" ;;
|
|
*) echo "$package" ;;
|
|
esac
|
|
;;
|
|
brew)
|
|
case "$package" in
|
|
docker) echo "docker" ;;
|
|
node) echo "node@22" ;;
|
|
postgres) echo "postgresql@17" ;;
|
|
*) echo "$package" ;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo "$package"
|
|
;;
|
|
esac
|
|
}
|