Add centralized `add_to_path` function to consistently update shell profile files (.profile, .bash_profile, .bashrc, .zshrc) with PATH exports. Update just and uv installation to use direct binary verification instead of relying on `command -v`, ensuring tools are properly installed even when PATH isn't immediately updated. Enhance platform installation by explicitly setting PATH and using full CLI path for execution.
910 lines
30 KiB
Bash
Executable File
910 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
|
||
# ============================================================================
|
||
|
||
run_platform_install() {
|
||
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 ""
|
||
|
||
log_info "Starting platform configuration and installation..."
|
||
echo ""
|
||
|
||
# Ensure PATH includes necessary directories for the internalai install command
|
||
# This is critical because internalai install may depend on just, uv, docker, etc.
|
||
export PATH="$HOME/.local/bin:$CLI_INSTALL_DIR:$PATH"
|
||
|
||
# Run internalai install using the full path
|
||
local cli_path=""
|
||
if [ -x "$CLI_INSTALL_DIR/$CLI_NAME" ]; then
|
||
cli_path="$CLI_INSTALL_DIR/$CLI_NAME"
|
||
elif command -v "$CLI_NAME" &> /dev/null; then
|
||
cli_path=$(command -v "$CLI_NAME")
|
||
else
|
||
log_error "$CLI_NAME command not found"
|
||
log_info "Expected location: $CLI_INSTALL_DIR/$CLI_NAME"
|
||
log_info "Please run manually: $CLI_NAME install"
|
||
exit 1
|
||
fi
|
||
|
||
log_info "Running: $cli_path install"
|
||
"$cli_path" install
|
||
}
|
||
|
||
# ============================================================================
|
||
# Main Installation Flow
|
||
# ============================================================================
|
||
|
||
main() {
|
||
show_welcome
|
||
|
||
check_dependencies
|
||
setup_gitea_auth
|
||
clone_repository
|
||
configure_git_credentials
|
||
install_cli
|
||
run_platform_install
|
||
|
||
echo ""
|
||
echo -e "${GREEN}${BOLD}Setup completed successfully!${NC}"
|
||
echo ""
|
||
}
|
||
|
||
main "$@"
|