901 lines
30 KiB
Bash
Executable File
901 lines
30 KiB
Bash
Executable File
#!/bin/bash
|
||
set -euo pipefail
|
||
|
||
# InternalAI Platform Installer
|
||
|
||
# ============================================================================
|
||
# Configuration
|
||
# ============================================================================
|
||
|
||
PLATFORM_NAME="InternalAI Platform"
|
||
DEFAULT_INSTALL_DIR="$HOME/internalai"
|
||
REPO_URL="https://gitea.app.monadical.io/monadical/internalai.git"
|
||
REPO_SSH="git@gitea.app.monadical.io:monadical/internalai.git"
|
||
CLI_INSTALL_DIR="/usr/local/bin"
|
||
CLI_NAME="internalai"
|
||
VERSION="1.0.0"
|
||
|
||
# Colors
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
BOLD='\033[1m'
|
||
NC='\033[0m'
|
||
|
||
# ============================================================================
|
||
# Logging Functions
|
||
# ============================================================================
|
||
|
||
log_info() {
|
||
echo -e "${BLUE}ℹ${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}⚠${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
}
|
||
|
||
log_header() {
|
||
echo ""
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
|
||
echo -e "${CYAN} $1${NC}"
|
||
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
|
||
echo ""
|
||
}
|
||
|
||
print_banner() {
|
||
clear
|
||
echo -e "${BOLD}${CYAN}"
|
||
cat << 'EOF'
|
||
╔═════════════════════════════════════════════════════════════════════════════════════════╗
|
||
║ ║
|
||
║ ██╗███╗ ██╗████████╗███████╗██████╗ ███╗ ██╗ █████╗ ██╗ █████╗ ██╗ ║
|
||
║ ██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗████╗ ██║██╔══██╗██║ ██╔══██╗██║ ║
|
||
║ ██║██╔██╗ ██║ ██║ █████╗ ██████╔╝██╔██╗ ██║███████║██║ ███████║██║ ║
|
||
║ ██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══██║██║ ██╔══██║██║ ║
|
||
║ ██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ╚████║██║ ██║███████╗ ██║ ██║██║ ║
|
||
║ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝╚═╝ ║
|
||
║ ║
|
||
║ Platform Installer v${VERSION} ║
|
||
╚═════════════════════════════════════════════════════════════════════════════════════════╝
|
||
EOF
|
||
echo -e "${NC}"
|
||
}
|
||
|
||
show_welcome() {
|
||
print_banner
|
||
log_info "This installer will:"
|
||
echo " • Check system dependencies"
|
||
echo " • Install missing dependencies (Git, Docker, just, uv)"
|
||
echo " • Set up Gitea authentication"
|
||
echo " • Clone the InternalAI platform repository"
|
||
echo " • Install the 'internalai' command-line tool"
|
||
echo ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# Utility Functions
|
||
# ============================================================================
|
||
|
||
add_to_path() {
|
||
local dir_to_add="$1"
|
||
local path_export="export PATH=\"$dir_to_add:\$PATH\""
|
||
|
||
# List of profile files to update (in order of preference)
|
||
local profile_files=(
|
||
"$HOME/.profile"
|
||
"$HOME/.bash_profile"
|
||
"$HOME/.bashrc"
|
||
"$HOME/.zshrc"
|
||
)
|
||
|
||
local added_to_file=false
|
||
|
||
for profile_file in "${profile_files[@]}"; do
|
||
# Only add to existing files or create .profile/.bashrc if they don't exist
|
||
if [ -f "$profile_file" ] || [[ "$profile_file" == *".profile" ]] || [[ "$profile_file" == *".bashrc" ]]; then
|
||
# Check if PATH is already in the file
|
||
if [ -f "$profile_file" ] && grep -q "export PATH=\"$dir_to_add:\$PATH\"" "$profile_file" 2>/dev/null; then
|
||
continue
|
||
fi
|
||
|
||
# Add PATH export to the file
|
||
echo "" >> "$profile_file"
|
||
echo "# Added by InternalAI installer" >> "$profile_file"
|
||
echo "$path_export" >> "$profile_file"
|
||
added_to_file=true
|
||
|
||
log_info "Added $dir_to_add to PATH in $profile_file"
|
||
fi
|
||
done
|
||
|
||
if [ "$added_to_file" = false ]; then
|
||
log_warning "Could not find suitable profile file to persist PATH"
|
||
log_info "Add this to your shell profile manually: $path_export"
|
||
fi
|
||
}
|
||
|
||
prompt() {
|
||
local varname=$1
|
||
local prompt_text=$2
|
||
local default_value=$3
|
||
local is_secret=${4:-false}
|
||
|
||
if [ -n "$default_value" ]; then
|
||
prompt_text="$prompt_text [${default_value}]"
|
||
fi
|
||
|
||
if [ "$is_secret" = true ]; then
|
||
echo -ne "${YELLOW}${prompt_text}: ${NC}" >&2
|
||
value=""
|
||
while IFS= read -r -s -n1 char < /dev/tty; do
|
||
if [[ $char == $'\0' ]]; then
|
||
break
|
||
fi
|
||
if [[ $char == $'\177' ]] || [[ $char == $'\b' ]]; then
|
||
if [ ${#value} -gt 0 ]; then
|
||
value="${value%?}"
|
||
echo -ne "\b \b" >&2
|
||
fi
|
||
else
|
||
value+="$char"
|
||
echo -n "*" >&2
|
||
fi
|
||
done
|
||
echo "" >&2
|
||
else
|
||
echo -ne "${YELLOW}${prompt_text}: ${NC}" >&2
|
||
read value < /dev/tty
|
||
fi
|
||
|
||
if [ -z "$value" ] && [ -n "$default_value" ]; then
|
||
value="$default_value"
|
||
fi
|
||
|
||
eval "$varname='$value'"
|
||
}
|
||
|
||
# ============================================================================
|
||
# Dependency Checking and Installation
|
||
# ============================================================================
|
||
|
||
check_dependencies() {
|
||
log_header "Checking Dependencies"
|
||
|
||
# Check Git
|
||
if command -v git &> /dev/null; then
|
||
log_success "git installed ($(git --version | head -n1))"
|
||
else
|
||
log_warning "git is not installed (will attempt to install)"
|
||
install_git
|
||
fi
|
||
|
||
# Check bash version
|
||
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
|
||
log_warning "Bash version ${BASH_VERSION} detected. Version 4+ recommended."
|
||
else
|
||
log_success "bash ${BASH_VERSION} is installed"
|
||
fi
|
||
|
||
# Check Docker
|
||
if command -v docker &> /dev/null; then
|
||
log_success "docker installed ($(docker --version | head -n1))"
|
||
else
|
||
log_warning "docker is not installed (will attempt to install)"
|
||
install_docker
|
||
fi
|
||
|
||
# Check Docker Compose (should be included with modern Docker installations)
|
||
if docker compose version &> /dev/null 2>&1; then
|
||
log_success "docker compose installed ($(docker compose version | head -n1))"
|
||
elif command -v docker-compose &> /dev/null; then
|
||
log_success "docker-compose installed ($(docker-compose --version | head -n1))"
|
||
else
|
||
log_warning "docker compose not found after Docker installation"
|
||
log_info "Docker Compose should be included with Docker. Verifying installation..."
|
||
# Try to verify docker is working first
|
||
if docker --version &> /dev/null; then
|
||
log_info "Docker is installed but compose plugin may not be available"
|
||
log_info "This is usually not an issue with modern Docker installations"
|
||
fi
|
||
fi
|
||
|
||
# Check Just
|
||
if command -v just &> /dev/null; then
|
||
log_success "just installed ($(just --version | head -n1))"
|
||
else
|
||
log_warning "just is not installed (will attempt to install)"
|
||
install_just
|
||
fi
|
||
|
||
# Check uv
|
||
if command -v uv &> /dev/null; then
|
||
log_success "uv installed ($(uv --version | head -n1))"
|
||
else
|
||
log_warning "uv is not installed (will attempt to install)"
|
||
install_uv
|
||
fi
|
||
|
||
log_success "All dependencies are installed!"
|
||
}
|
||
|
||
install_just() {
|
||
# Try package managers first
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
if command -v brew &> /dev/null; then
|
||
log_info "Installing just via Homebrew..."
|
||
if brew install just; then
|
||
log_success "just installed"
|
||
return 0
|
||
else
|
||
log_warning "Homebrew installation failed, trying binary download..."
|
||
fi
|
||
fi
|
||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||
if command -v cargo &> /dev/null; then
|
||
log_info "Installing just via cargo..."
|
||
if cargo install just; then
|
||
log_success "just installed"
|
||
return 0
|
||
else
|
||
log_warning "Cargo installation failed, trying binary download..."
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Fall back to downloading pre-built binary
|
||
log_info "Downloading pre-built just binary..."
|
||
|
||
# Detect architecture
|
||
local arch=$(uname -m)
|
||
local os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||
|
||
# Map architecture names
|
||
case "$arch" in
|
||
x86_64) arch="x86_64" ;;
|
||
aarch64|arm64) arch="aarch64" ;;
|
||
*)
|
||
log_error "Unsupported architecture: $arch"
|
||
log_info "Please install just manually: https://github.com/casey/just#installation"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# Map OS names
|
||
case "$os" in
|
||
darwin) os="apple-darwin" ;;
|
||
linux) os="unknown-linux-musl" ;;
|
||
*)
|
||
log_error "Unsupported OS: $os"
|
||
log_info "Please install just manually: https://github.com/casey/just#installation"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# Get latest version from GitHub API
|
||
log_info "Fetching latest version..."
|
||
local latest_version=$(curl -fsSL https://api.github.com/repos/casey/just/releases/latest | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/')
|
||
|
||
if [ -z "$latest_version" ]; then
|
||
log_error "Failed to fetch latest version"
|
||
log_info "Please install just manually: https://github.com/casey/just#installation"
|
||
exit 1
|
||
fi
|
||
|
||
local binary_name="just-${latest_version}-${arch}-${os}"
|
||
local download_url="https://github.com/casey/just/releases/download/${latest_version}/${binary_name}.tar.gz"
|
||
local install_dir="$HOME/.local/bin"
|
||
|
||
# Create install directory
|
||
mkdir -p "$install_dir"
|
||
|
||
# Download and extract
|
||
local tmp_dir=$(mktemp -d)
|
||
trap "rm -rf $tmp_dir" EXIT
|
||
|
||
log_info "Downloading from: $download_url"
|
||
if ! curl -fsSL "$download_url" -o "$tmp_dir/just.tar.gz"; then
|
||
log_error "Failed to download just binary"
|
||
log_info "Please install just manually: https://github.com/casey/just#installation"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Extracting to $install_dir..."
|
||
if ! tar -xzf "$tmp_dir/just.tar.gz" -C "$tmp_dir"; then
|
||
log_error "Failed to extract just binary"
|
||
exit 1
|
||
fi
|
||
|
||
# Install binary
|
||
if ! mv "$tmp_dir/just" "$install_dir/just"; then
|
||
log_error "Failed to install just to $install_dir"
|
||
exit 1
|
||
fi
|
||
|
||
chmod +x "$install_dir/just"
|
||
|
||
# Add to PATH if not already there
|
||
if [[ ":$PATH:" != *":$install_dir:"* ]]; then
|
||
log_info "Adding $install_dir to PATH..."
|
||
export PATH="$install_dir:$PATH"
|
||
fi
|
||
|
||
# Persist PATH to shell profiles
|
||
add_to_path "$install_dir"
|
||
|
||
# Verify installation by checking the binary directly
|
||
if [ -x "$install_dir/just" ]; then
|
||
log_success "just installed successfully to $install_dir/just"
|
||
else
|
||
log_error "just binary not found or not executable at $install_dir/just"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_uv() {
|
||
log_info "Installing uv (Python package manager)..."
|
||
|
||
local install_dir="$HOME/.local/bin"
|
||
|
||
if curl -LsSf https://astral.sh/uv/install.sh | sh; then
|
||
# Add uv to PATH for current session
|
||
if [[ ":$PATH:" != *":$install_dir:"* ]]; then
|
||
export PATH="$install_dir:$PATH"
|
||
fi
|
||
|
||
# Persist PATH to shell profiles
|
||
add_to_path "$install_dir"
|
||
|
||
# Verify installation by checking the binary directly
|
||
if [ -x "$install_dir/uv" ]; then
|
||
log_success "uv installed successfully to $install_dir/uv"
|
||
else
|
||
log_error "uv binary not found or not executable at $install_dir/uv"
|
||
exit 1
|
||
fi
|
||
else
|
||
log_error "Failed to install uv"
|
||
log_info "Please install manually: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
detect_linux_distro() {
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
echo "$ID"
|
||
elif [ -f /etc/redhat-release ]; then
|
||
echo "rhel"
|
||
elif [ -f /etc/debian_version ]; then
|
||
echo "debian"
|
||
else
|
||
echo "unknown"
|
||
fi
|
||
}
|
||
|
||
install_git() {
|
||
log_info "Installing Git..."
|
||
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
install_git_macos
|
||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||
install_git_linux
|
||
else
|
||
log_error "Unsupported OS: $OSTYPE"
|
||
log_info "Please install Git manually: https://git-scm.com/downloads"
|
||
exit 1
|
||
fi
|
||
|
||
# Verify installation
|
||
if command -v git &> /dev/null; then
|
||
log_success "Git installed successfully ($(git --version | head -n1))"
|
||
else
|
||
log_error "Git installation failed"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_git_macos() {
|
||
if command -v brew &> /dev/null; then
|
||
log_info "Installing Git via Homebrew..."
|
||
if brew install git; then
|
||
log_success "Git installed via Homebrew"
|
||
else
|
||
log_error "Failed to install Git via Homebrew"
|
||
exit 1
|
||
fi
|
||
else
|
||
# Try Xcode Command Line Tools
|
||
log_info "Installing Xcode Command Line Tools (includes Git)..."
|
||
log_info "A dialog will appear - please click 'Install'"
|
||
if xcode-select --install 2>&1 | grep -q "already installed"; then
|
||
log_info "Xcode Command Line Tools already installed"
|
||
else
|
||
log_info "Waiting for Xcode Command Line Tools installation..."
|
||
log_info "This may take a few minutes. Please complete the installation dialog."
|
||
|
||
# Wait for installation to complete
|
||
until xcode-select -p &> /dev/null; do
|
||
sleep 5
|
||
done
|
||
log_success "Xcode Command Line Tools installed"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
install_git_linux() {
|
||
local distro=$(detect_linux_distro)
|
||
|
||
log_info "Installing Git on $distro..."
|
||
|
||
case "$distro" in
|
||
ubuntu|debian|pop|linuxmint)
|
||
sudo apt-get update
|
||
sudo apt-get install -y git
|
||
;;
|
||
fedora|rhel|centos|rocky|almalinux)
|
||
if command -v dnf &> /dev/null; then
|
||
sudo dnf install -y git
|
||
else
|
||
sudo yum install -y git
|
||
fi
|
||
;;
|
||
arch|manjaro)
|
||
sudo pacman -Sy --noconfirm git
|
||
;;
|
||
opensuse*|sles)
|
||
sudo zypper install -y git
|
||
;;
|
||
alpine)
|
||
sudo apk add git
|
||
;;
|
||
*)
|
||
log_error "Unsupported Linux distribution: $distro"
|
||
log_info "Please install Git manually for your distribution:"
|
||
log_info " https://git-scm.com/download/linux"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
install_docker() {
|
||
log_info "Installing Docker..."
|
||
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
install_docker_macos
|
||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||
install_docker_linux
|
||
else
|
||
log_error "Unsupported OS: $OSTYPE"
|
||
log_info "Please install Docker manually: https://docs.docker.com/get-docker/"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_docker_macos() {
|
||
if command -v brew &> /dev/null; then
|
||
log_info "Installing Docker via Homebrew..."
|
||
if brew install --cask docker; then
|
||
log_success "Docker installed via Homebrew"
|
||
log_info "Starting Docker Desktop..."
|
||
open -a Docker
|
||
log_info "Waiting for Docker to start..."
|
||
|
||
# Wait for docker to be ready (up to 60 seconds)
|
||
local timeout=60
|
||
local elapsed=0
|
||
while ! docker info &> /dev/null; do
|
||
if [ $elapsed -ge $timeout ]; then
|
||
log_warning "Docker daemon did not start within ${timeout}s"
|
||
log_info "Please start Docker Desktop manually and run the installer again"
|
||
exit 1
|
||
fi
|
||
sleep 2
|
||
elapsed=$((elapsed + 2))
|
||
done
|
||
|
||
log_success "Docker is ready"
|
||
else
|
||
log_error "Failed to install Docker via Homebrew"
|
||
log_info "Please install Docker Desktop manually: https://docs.docker.com/desktop/install/mac-install/"
|
||
exit 1
|
||
fi
|
||
else
|
||
log_error "Homebrew is not installed"
|
||
log_info "Install Homebrew first: https://brew.sh"
|
||
log_info "Or install Docker Desktop manually: https://docs.docker.com/desktop/install/mac-install/"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_docker_linux() {
|
||
local distro=$(detect_linux_distro)
|
||
|
||
log_info "Detected Linux distribution: $distro"
|
||
|
||
case "$distro" in
|
||
ubuntu|debian|pop|linuxmint)
|
||
install_docker_debian
|
||
;;
|
||
fedora|rhel|centos|rocky|almalinux)
|
||
install_docker_rhel
|
||
;;
|
||
arch|manjaro)
|
||
install_docker_arch
|
||
;;
|
||
*)
|
||
log_error "Unsupported Linux distribution: $distro"
|
||
log_info "Please install Docker manually: https://docs.docker.com/engine/install/"
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
# Start and enable Docker service
|
||
start_docker_service
|
||
|
||
# Add user to docker group and activate immediately
|
||
setup_docker_permissions
|
||
}
|
||
|
||
install_docker_debian() {
|
||
log_info "Installing Docker on Debian/Ubuntu..."
|
||
|
||
# Update package index
|
||
sudo apt-get update
|
||
|
||
# Install prerequisites
|
||
sudo apt-get install -y ca-certificates curl gnupg lsb-release
|
||
|
||
# Add Docker's official GPG key
|
||
sudo install -m 0755 -d /etc/apt/keyrings
|
||
curl -fsSL https://download.docker.com/linux/$(detect_linux_distro)/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||
|
||
# Set up the repository
|
||
echo \
|
||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$(detect_linux_distro) \
|
||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||
|
||
# Install Docker Engine
|
||
sudo apt-get update
|
||
if sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then
|
||
log_success "Docker installed successfully"
|
||
else
|
||
log_error "Failed to install Docker"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_docker_rhel() {
|
||
log_info "Installing Docker on RHEL/Fedora/CentOS..."
|
||
|
||
# Remove old versions
|
||
sudo dnf remove -y docker docker-client docker-client-latest docker-common docker-latest \
|
||
docker-latest-logrotate docker-logrotate docker-engine podman runc || true
|
||
|
||
# Install prerequisites
|
||
sudo dnf install -y dnf-plugins-core
|
||
|
||
# Add Docker repository
|
||
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
||
|
||
# Install Docker Engine
|
||
if sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; then
|
||
log_success "Docker installed successfully"
|
||
else
|
||
log_error "Failed to install Docker"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
install_docker_arch() {
|
||
log_info "Installing Docker on Arch Linux..."
|
||
|
||
# Install Docker from official repositories
|
||
if sudo pacman -Sy --noconfirm docker docker-compose; then
|
||
log_success "Docker installed successfully"
|
||
else
|
||
log_error "Failed to install Docker"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
start_docker_service() {
|
||
log_info "Starting Docker service..."
|
||
|
||
if command -v systemctl &> /dev/null; then
|
||
sudo systemctl start docker
|
||
sudo systemctl enable docker
|
||
log_success "Docker service started and enabled"
|
||
else
|
||
log_warning "systemctl not found, please start Docker manually"
|
||
fi
|
||
}
|
||
|
||
setup_docker_permissions() {
|
||
log_info "Setting up Docker permissions..."
|
||
|
||
# Add current user to docker group
|
||
if ! groups | grep -q docker; then
|
||
sudo usermod -aG docker "$USER"
|
||
log_success "User added to docker group"
|
||
fi
|
||
|
||
# Test docker access
|
||
if docker ps &> /dev/null 2>&1; then
|
||
log_success "Docker is accessible without sudo"
|
||
return 0
|
||
fi
|
||
|
||
# If docker requires sudo, set up for immediate use
|
||
if sudo docker ps &> /dev/null 2>&1; then
|
||
log_info "Docker group membership will be active in new terminal sessions"
|
||
log_info "For this session, using docker with sudo privileges..."
|
||
|
||
# Create a wrapper function for the current script session
|
||
# This allows the rest of the script to use docker commands
|
||
docker() {
|
||
sudo /usr/bin/docker "$@"
|
||
}
|
||
export -f docker 2>/dev/null || true
|
||
|
||
log_success "Docker is ready to use (with sudo for current session)"
|
||
log_info "Note: New terminal sessions will have docker access without sudo"
|
||
else
|
||
log_error "Docker is not accessible"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Gitea Authentication
|
||
# ============================================================================
|
||
|
||
AUTH_TYPE=""
|
||
GITEA_TOKEN=""
|
||
|
||
setup_gitea_auth() {
|
||
log_header "Gitea Authentication"
|
||
|
||
log_info "The InternalAI platform repository is private and requires authentication."
|
||
echo ""
|
||
log_info "Choose authentication method:"
|
||
echo " 1) Personal Access Token (recommended)"
|
||
echo " 2) SSH Key (if already configured)"
|
||
echo ""
|
||
|
||
prompt AUTH_CHOICE "Select authentication method" "1"
|
||
|
||
case "$AUTH_CHOICE" in
|
||
1)
|
||
AUTH_TYPE="token"
|
||
echo ""
|
||
log_info "To create a personal access token:"
|
||
log_info " 1. Go to: https://gitea.app.monadical.io/user/settings/applications"
|
||
log_info " 2. Click 'Generate New Token'"
|
||
log_info " 3. Give it a name (e.g., 'InternalAI Setup')"
|
||
log_info " 4. Give it Read repository scope"
|
||
log_info " 5. Click 'Generate Token' and copy it"
|
||
echo ""
|
||
prompt GITEA_TOKEN "Enter your Gitea Personal Access Token" "" true
|
||
|
||
if [ -z "$GITEA_TOKEN" ]; then
|
||
log_error "Token is required"
|
||
exit 1
|
||
fi
|
||
;;
|
||
2)
|
||
AUTH_TYPE="ssh"
|
||
log_info "Using SSH authentication"
|
||
log_info "Ensure your SSH key is added to Gitea:"
|
||
log_info " https://gitea.app.monadical.io/user/settings/keys"
|
||
|
||
# Test SSH connection
|
||
echo ""
|
||
log_info "Testing SSH connection..."
|
||
if ssh -T git@gitea.app.monadical.io 2>&1 | grep -q "Hi there"; then
|
||
log_success "SSH connection successful"
|
||
else
|
||
log_warning "Could not verify SSH connection, but will try to continue..."
|
||
fi
|
||
;;
|
||
*)
|
||
log_error "Invalid choice"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
prepare_git_url() {
|
||
if [ "$AUTH_TYPE" = "token" ] && [ -n "$GITEA_TOKEN" ]; then
|
||
echo "$REPO_URL" | sed "s|https://gitea.app.monadical.io/|https://${GITEA_TOKEN}@gitea.app.monadical.io/|"
|
||
elif [ "$AUTH_TYPE" = "ssh" ]; then
|
||
echo "$REPO_SSH"
|
||
else
|
||
echo "$REPO_URL"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Git Credential Caching
|
||
# ============================================================================
|
||
|
||
configure_git_credentials() {
|
||
log_header "Configuring Git Credentials"
|
||
|
||
if [ "$AUTH_TYPE" = "ssh" ]; then
|
||
log_info "Using SSH - no credential caching needed"
|
||
return
|
||
fi
|
||
|
||
# Detect OS and configure appropriate credential helper
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
# macOS - use keychain
|
||
log_info "Configuring git to use macOS Keychain..."
|
||
git config --global credential.helper osxkeychain
|
||
log_success "Git credentials will be stored in macOS Keychain"
|
||
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||
# Linux - try to use libsecret, fall back to cache
|
||
if command -v git-credential-libsecret &> /dev/null; then
|
||
log_info "Configuring git to use libsecret..."
|
||
git config --global credential.helper libsecret
|
||
log_success "Git credentials will be stored in system keyring"
|
||
else
|
||
log_info "Configuring git credential cache (1 hour)..."
|
||
git config --global credential.helper 'cache --timeout=3600'
|
||
log_success "Git credentials will be cached in memory for 1 hour"
|
||
log_info "For persistent storage, install: libsecret"
|
||
fi
|
||
else
|
||
# Unknown OS - use cache
|
||
log_info "Configuring git credential cache (1 hour)..."
|
||
git config --global credential.helper 'cache --timeout=3600'
|
||
log_success "Git credentials will be cached in memory for 1 hour"
|
||
fi
|
||
|
||
# Store credentials by triggering a git operation
|
||
if [ "$AUTH_TYPE" = "token" ] && [ -n "$INSTALL_DIR" ] && [ -d "$INSTALL_DIR/.git" ]; then
|
||
log_info "Caching credentials..."
|
||
cd "$INSTALL_DIR"
|
||
# Trigger credential storage by doing a fetch (which uses credentials)
|
||
git fetch --dry-run 2>&1 | grep -v "password\|token" > /dev/null || true
|
||
log_success "Credentials cached successfully"
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Repository Cloning
|
||
# ============================================================================
|
||
|
||
clone_repository() {
|
||
log_header "Cloning Repository"
|
||
|
||
prompt INSTALL_DIR "Installation directory" "$DEFAULT_INSTALL_DIR"
|
||
|
||
if [ -d "$INSTALL_DIR" ]; then
|
||
if [ -d "$INSTALL_DIR/.git" ]; then
|
||
log_warning "Directory already exists and appears to be a git repository"
|
||
prompt UPDATE_CHOICE "Update existing repository? (y/n)" "y"
|
||
if [[ "$UPDATE_CHOICE" =~ ^[Yy] ]]; then
|
||
log_info "Pulling latest changes..."
|
||
cd "$INSTALL_DIR"
|
||
git pull
|
||
log_success "Repository updated"
|
||
return
|
||
else
|
||
log_info "Using existing repository"
|
||
return
|
||
fi
|
||
else
|
||
log_error "Directory exists but is not a git repository"
|
||
log_info "Please remove or rename: $INSTALL_DIR"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
local git_url=$(prepare_git_url)
|
||
|
||
log_info "Cloning repository to: $INSTALL_DIR"
|
||
log_info "This may take a few minutes..."
|
||
|
||
if git clone "$git_url" "$INSTALL_DIR" 2>&1 | grep -v "password\|token"; then
|
||
log_success "Repository cloned successfully"
|
||
else
|
||
log_error "Failed to clone repository"
|
||
log_info "Please check your credentials and try again"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# CLI Installation
|
||
# ============================================================================
|
||
|
||
install_cli() {
|
||
log_header "Installing CLI Tool"
|
||
|
||
local cli_script="$INSTALL_DIR/scripts/$CLI_NAME"
|
||
|
||
if [ ! -f "$cli_script" ]; then
|
||
log_error "CLI script not found at: $cli_script"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Installing $CLI_NAME to $CLI_INSTALL_DIR..."
|
||
|
||
if [ -w "$CLI_INSTALL_DIR" ]; then
|
||
cp "$cli_script" "$CLI_INSTALL_DIR/$CLI_NAME"
|
||
chmod +x "$CLI_INSTALL_DIR/$CLI_NAME"
|
||
else
|
||
log_warning "Need sudo permissions to write to $CLI_INSTALL_DIR"
|
||
sudo cp "$cli_script" "$CLI_INSTALL_DIR/$CLI_NAME"
|
||
sudo chmod +x "$CLI_INSTALL_DIR/$CLI_NAME"
|
||
fi
|
||
|
||
if command -v "$CLI_NAME" &> /dev/null; then
|
||
log_success "$CLI_NAME installed successfully!"
|
||
else
|
||
log_warning "$CLI_NAME installed but not found in PATH"
|
||
log_info "You may need to restart your terminal or run:"
|
||
log_info " export PATH=\"$CLI_INSTALL_DIR:\$PATH\""
|
||
fi
|
||
}
|
||
|
||
# ============================================================================
|
||
# Post-Installation
|
||
# ============================================================================
|
||
|
||
show_next_steps() {
|
||
log_header "Installation Complete!"
|
||
|
||
echo -e "${GREEN}${BOLD}The InternalAI platform has been set up successfully!${NC}"
|
||
echo ""
|
||
echo -e "${BOLD}Platform Location:${NC} ${CYAN}$INSTALL_DIR${NC}"
|
||
echo ""
|
||
echo -e "${BOLD}Next Steps:${NC}"
|
||
echo ""
|
||
echo " 1. Configure and start the platform:"
|
||
echo -e " ${GREEN}$CLI_NAME install${NC}"
|
||
echo ""
|
||
echo " 2. View available commands:"
|
||
echo -e " ${GREEN}$CLI_NAME help${NC}"
|
||
echo ""
|
||
echo " 3. Check platform status:"
|
||
echo -e " ${GREEN}$CLI_NAME status${NC}"
|
||
echo ""
|
||
echo -e "${BOLD}Documentation:${NC}"
|
||
echo " • Platform docs: $INSTALL_DIR/docs/"
|
||
echo " • README: $INSTALL_DIR/README.md"
|
||
echo ""
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main Installation Flow
|
||
# ============================================================================
|
||
|
||
main() {
|
||
show_welcome
|
||
|
||
check_dependencies
|
||
setup_gitea_auth
|
||
clone_repository
|
||
configure_git_credentials
|
||
install_cli
|
||
show_next_steps
|
||
|
||
echo -e "${GREEN}${BOLD}Setup completed successfully!${NC}"
|
||
echo ""
|
||
}
|
||
|
||
main "$@"
|