diff --git a/README.md b/README.md index a7c7ccd..aa427e9 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,12 @@ curl -fsSL https://gitea.app.monadical.io/jose/internalai-setup/raw/branch/main/ ``` This will: -1. Check system dependencies -2. Prompt for Gitea authentication -3. Clone the InternalAI platform repository -4. Install the `internalai` command-line tool +1. Check system dependencies (git, docker, docker compose, bash) +2. Install missing dependencies (just, uv) +3. Set up Gitea authentication +4. Clone the InternalAI platform repository +5. Configure git credentials +6. Install the `internalai` command-line tool ## Prerequisites @@ -24,6 +26,7 @@ Before installing, ensure you have: - **Docker** - Container runtime (v20.10+) - **Docker Compose** - Container orchestration (v2.0+) - **Just** - Command runner (will auto-install if missing) +- **uv** - Python package manager (will auto-install if missing) ### Platform Requirements @@ -236,7 +239,8 @@ If the automated installer doesn't work, you can install manually: 2. Install the CLI: ```bash - ./scripts/install.sh + sudo cp scripts/internalai /usr/local/bin/internalai + sudo chmod +x /usr/local/bin/internalai ``` 3. Configure the platform: @@ -262,11 +266,17 @@ sudo rm /usr/local/bin/internalai # Remove CLI ``` internalai-setup/ -├── install.sh # One-liner installer -├── setup.sh # Main setup script +├── install.sh # Complete installation script └── README.md # This file ``` +The installer handles: +- Dependency checking and installation (just, uv) +- Gitea authentication (token or SSH) +- Repository cloning +- Git credential configuration +- CLI tool installation + The actual platform code is in the private `internalai` repository. ### Contributing diff --git a/install.sh b/install.sh index 0d82052..2aa2454 100755 --- a/install.sh +++ b/install.sh @@ -8,14 +8,21 @@ set -euo pipefail # Configuration # ============================================================================ -SETUP_URL="https://gitea.app.monadical.io/jose/internalai-setup/raw/branch/main/setup.sh" -INSTALLER_VERSION="1.0.0" +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' # ============================================================================ @@ -40,32 +47,119 @@ log_error() { log_header() { echo "" - echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" - echo -e "${BLUE} $1${NC}" - echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" + 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 (just, uv)" + echo " • Set up Gitea authentication" + echo " • Clone the InternalAI platform repository" + echo " • Install the 'internalai' command-line tool" echo "" } # ============================================================================ -# Installer Functions +# Utility Functions # ============================================================================ -check_requirements() { - log_header "Checking Requirements" +prompt() { + local varname=$1 + local prompt_text=$2 + local default_value=$3 + local is_secret=${4:-false} - # Check curl - if command -v curl &> /dev/null; then - log_success "curl is installed" + 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 - log_error "curl is required but not installed" - log_info "Install curl and try again:" - if [[ "$OSTYPE" == "darwin"* ]]; then - log_info " brew install curl" - else - log_info " sudo apt-get install curl # Debian/Ubuntu" - log_info " sudo yum install curl # RHEL/CentOS" - fi - exit 1 + 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" + + local deps_ok=true + + # Check Git + if command -v git &> /dev/null; then + log_success "git installed ($(git --version | head -n1))" + else + log_error "git is not installed" + deps_ok=false + fi + + # Check Docker + if command -v docker &> /dev/null; then + log_success "docker installed ($(docker --version | head -n1))" + else + log_error "docker is not installed" + deps_ok=false + fi + + # Check Docker Compose + 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_error "docker compose is not installed" + deps_ok=false fi # Check bash version @@ -74,61 +168,398 @@ check_requirements() { else log_success "bash ${BASH_VERSION} is installed" 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 + + if [ "$deps_ok" = false ]; then + log_error "Please install missing dependencies and try again" + echo "" + log_info "Installation guides:" + log_info " • Git: https://git-scm.com/downloads" + log_info " • Docker: https://docs.docker.com/get-docker/" + exit 1 + fi } -download_and_run_setup() { - log_header "Downloading Setup Script" +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 - # Create temporary file - TMP_FILE=$(mktemp) - trap "rm -f $TMP_FILE" EXIT + # Fall back to downloading pre-built binary + log_info "Downloading pre-built just binary..." - log_info "Downloading setup script from:" - log_info " $SETUP_URL" - echo "" + # Detect architecture + local arch=$(uname -m) + local os=$(uname -s | tr '[:upper:]' '[:lower:]') - # Download the script - if ! curl -fsSL "$SETUP_URL" -o "$TMP_FILE"; then - log_error "Failed to download setup script" - log_info "Please check:" - log_info " • Your internet connection" - log_info " • The repository URL is correct" - log_info " • You have access to the repository" + # 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 - # Verify it's a valid bash script - if ! head -n 1 "$TMP_FILE" | grep -q "^#!/"; then - log_error "Downloaded file does not appear to be a valid script" + 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_success "Setup script downloaded successfully" + 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 - # Run the setup script - log_header "Running Setup" - echo "" + # 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 "$TMP_FILE" - bash "$TMP_FILE" "$@" + 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" + + # Add to shell profile + local shell_profile="" + if [ -n "$BASH_VERSION" ]; then + shell_profile="$HOME/.bashrc" + elif [ -n "$ZSH_VERSION" ]; then + shell_profile="$HOME/.zshrc" + fi + + if [ -n "$shell_profile" ]; then + echo "export PATH=\"$install_dir:\$PATH\"" >> "$shell_profile" + log_info "Added to $shell_profile (restart shell or run: source $shell_profile)" + fi + fi + + # Verify installation + if command -v just &> /dev/null; then + log_success "just installed successfully to $install_dir/just" + else + log_warning "just installed to $install_dir/just but not found in PATH" + log_info "You may need to restart your terminal or run:" + log_info " export PATH=\"$install_dir:\$PATH\"" + fi } -show_welcome() { - clear +install_uv() { + log_info "Installing uv (Python package manager)..." + + if curl -LsSf https://astral.sh/uv/install.sh | sh; then + # Add uv to PATH for current session + export PATH="$HOME/.local/bin:$PATH" + + if command -v uv &> /dev/null; then + log_success "uv installed successfully ($(uv --version | head -n1))" + else + log_error "uv installation succeeded but not found in PATH" + log_info "You may need to restart your terminal or run:" + log_info " export PATH=\"\$HOME/.local/bin:\$PATH\"" + 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 +} + +# ============================================================================ +# 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 "" - echo -e "${BLUE}╔══════════════════════════════════════════════════════╗${NC}" - echo -e "${BLUE}║ ║${NC}" - echo -e "${BLUE}║ ${GREEN}InternalAI Platform Installer${BLUE} ║${NC}" - echo -e "${BLUE}║ ║${NC}" - echo -e "${BLUE}║ Version ${INSTALLER_VERSION} ║${NC}" - echo -e "${BLUE}║ ║${NC}" - echo -e "${BLUE}╚══════════════════════════════════════════════════════╝${NC}" + log_info "Choose authentication method:" + echo " 1) Personal Access Token (recommended)" + echo " 2) SSH Key (if already configured)" echo "" - log_info "This installer will:" - echo " • Check system dependencies" - echo " • Set up Gitea authentication" - echo " • Clone the InternalAI platform repository" - echo " • Install the 'internalai' command-line tool" + + 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 "" } @@ -138,8 +569,16 @@ show_welcome() { main() { show_welcome - check_requirements - download_and_run_setup "$@" + + 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 "$@" diff --git a/setup.sh b/setup.sh deleted file mode 100755 index b9c4170..0000000 --- a/setup.sh +++ /dev/null @@ -1,540 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# InternalAI Platform Setup Script -# This script handles authentication and clones the private internalai monorepo - -# ============================================================================ -# 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 Setup v${VERSION} ║ -╚═════════════════════════════════════════════════════════════════════════════════════════╝ -EOF - echo -e "${NC}" -} - -# ============================================================================ -# Utility Functions -# ============================================================================ - -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 -# ============================================================================ - -check_dependencies() { - log_header "Checking Dependencies" - - local deps_ok=true - - # Check Git - if command -v git &> /dev/null; then - log_success "git installed ($(git --version | head -n1))" - else - log_error "git is not installed" - deps_ok=false - fi - - # Check Docker - if command -v docker &> /dev/null; then - log_success "docker installed ($(docker --version | head -n1))" - else - log_error "docker is not installed" - deps_ok=false - fi - - # Check Docker Compose - 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_error "docker compose is not installed" - deps_ok=false - 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 - - if [ "$deps_ok" = false ]; then - log_error "Please install missing dependencies and try again" - echo "" - log_info "Installation guides:" - log_info " • Git: https://git-scm.com/downloads" - log_info " • Docker: https://docs.docker.com/get-docker/" - log_info " • Just: https://github.com/casey/just#installation" - exit 1 - fi -} - -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" - - # Add to shell profile - local shell_profile="" - if [ -n "$BASH_VERSION" ]; then - shell_profile="$HOME/.bashrc" - elif [ -n "$ZSH_VERSION" ]; then - shell_profile="$HOME/.zshrc" - fi - - if [ -n "$shell_profile" ]; then - echo "export PATH=\"$install_dir:\$PATH\"" >> "$shell_profile" - log_info "Added to $shell_profile (restart shell or run: source $shell_profile)" - fi - fi - - # Verify installation - if command -v just &> /dev/null; then - log_success "just installed successfully to $install_dir/just" - else - log_warning "just installed to $install_dir/just but not found in PATH" - log_info "You may need to restart your terminal or run:" - log_info " export PATH=\"$install_dir:\$PATH\"" - 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() { - print_banner - - log_info "Starting InternalAI Platform setup..." - echo "" - - 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 "$@"