#!/bin/bash set -e # Exit immediately if a command exits with a non-zero status. # --- Configuration --- GITHUB_REPO="kusl/GeminiClient" INSTALL_DIR="/opt/gemini-client" # The specific binary name generated by the CI workflow (e.g., gemini-client-linux-x64) BINARY_BASE_NAME="gemini-client" SYMLINK_NAME="gemini-client" CONFIG_FILE="appsettings.json" # --- Helper Functions --- function check_deps() { echo "--> Checking for dependencies..." local deps=("curl" "jq" "tar") local missing=() for dep in "${deps[@]}"; do if ! command -v "$dep" &> /dev/null; then missing+=("$dep") fi done if [ ${#missing[@]} -gt 0 ]; then echo "Error: The following dependencies are required: ${missing[*]}" echo "Please install them using your package manager." echo " - Fedora/RHEL: sudo dnf install ${missing[*]}" echo " - Debian/Ubuntu: sudo apt-get update && sudo apt-get install ${missing[*]}" exit 1 fi } function detect_arch() { echo "--> Detecting system architecture..." ARCH=$(uname -m) case $ARCH in x86_64) GH_ARCH="linux-x64" ;; aarch64) GH_ARCH="linux-arm64" echo "⚠️ Note: While the project supports ARM64, the current CI pipeline might only publish x64 releases." ;; armv7l) GH_ARCH="linux-arm" ;; *) echo "Error: Unsupported architecture '$ARCH'." exit 1 ;; esac echo " Architecture found: $GH_ARCH" } # --- Main Script --- check_deps detect_arch # The asset name in GitHub releases (e.g., gemini-client-linux-x64.tar.gz) ASSET_NAME="${BINARY_BASE_NAME}-${GH_ARCH}.tar.gz" # The actual executable name inside the tar (e.g., gemini-client-linux-x64) EXECUTABLE_NAME="${BINARY_BASE_NAME}-${GH_ARCH}" echo "--> Fetching latest release information from GitHub..." API_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest" RELEASE_JSON=$(curl -s "$API_URL") DOWNLOAD_URL=$(echo "$RELEASE_JSON" | jq -r ".assets[] | select(.name == \"$ASSET_NAME\") | .browser_download_url") if [[ -z "$DOWNLOAD_URL" || "$DOWNLOAD_URL" == "null" ]]; then echo "Error: Could not find a download URL for asset '$ASSET_NAME' in the latest release." echo "This may mean there is no release for your architecture ($GH_ARCH) yet." echo "Please check the releases page: https://github.com/$GITHUB_REPO/releases" exit 1 fi VERSION=$(echo "$RELEASE_JSON" | jq -r .tag_name) echo " Found version $VERSION at: $DOWNLOAD_URL" # Create temporary directories TMP_DIR=$(mktemp -d) trap 'rm -rf -- "$TMP_DIR"' EXIT TMP_ARCHIVE="$TMP_DIR/$ASSET_NAME" echo "--> Downloading..." curl -L -o "$TMP_ARCHIVE" "$DOWNLOAD_URL" echo "--> Extracting..." EXTRACT_DIR="$TMP_DIR/extracted" mkdir -p "$EXTRACT_DIR" tar -xzf "$TMP_ARCHIVE" -C "$EXTRACT_DIR" # --- Configuration Management --- DEST_CONFIG_PATH="$INSTALL_DIR/$CONFIG_FILE" TEMP_CONFIG_BACKUP="$TMP_DIR/$CONFIG_FILE.bak" CONFIG_STRATEGY="none" # 1. Check if user already has a config if [ -f "$DEST_CONFIG_PATH" ]; then echo "--> Found existing configuration. Preserving it." sudo cp "$DEST_CONFIG_PATH" "$TEMP_CONFIG_BACKUP" CONFIG_STRATEGY="restore" else # 2. Check if the download included a config (Note: current CI excludes it) if [ -f "$EXTRACT_DIR/$CONFIG_FILE" ]; then echo "--> Using configuration provided in release." CONFIG_STRATEGY="copy_new" else # 3. Generate default config if missing echo "--> No configuration found in release. Generating default $CONFIG_FILE." cat < "$EXTRACT_DIR/$CONFIG_FILE" { "Logging": { "LogLevel": { "Default": "Warning", "GeminiClient": "Warning", "GeminiClientConsole": "Information" } }, "GeminiSettings": { "ApiKey": "YOUR_GEMINI_API_KEY_HERE", "BaseUrl": "https://generativelanguage.googleapis.com/", "DefaultModel": "gemini-2.5-flash", "StreamingEnabled": true } } EOF CONFIG_STRATEGY="copy_generated" fi fi echo "--> Installing to $INSTALL_DIR (requires sudo)..." # Prepare directory sudo mkdir -p "$INSTALL_DIR" # Copy executable sudo cp "$EXTRACT_DIR/$EXECUTABLE_NAME" "$INSTALL_DIR/" # Handle Config Placement if [ "$CONFIG_STRATEGY" == "restore" ]; then sudo mv "$TEMP_CONFIG_BACKUP" "$DEST_CONFIG_PATH" elif [ "$CONFIG_STRATEGY" == "copy_generated" ] || [ "$CONFIG_STRATEGY" == "copy_new" ]; then sudo cp "$EXTRACT_DIR/$CONFIG_FILE" "$DEST_CONFIG_PATH" fi # Set permissions INSTALLED_EXEC="$INSTALL_DIR/$EXECUTABLE_NAME" echo "--> Setting executable permissions..." sudo chmod +x "$INSTALLED_EXEC" # Ensure config is readable by users sudo chmod 644 "$DEST_CONFIG_PATH" # Symlink echo "--> Updating symbolic link..." sudo ln -sf "$INSTALLED_EXEC" "/usr/local/bin/$SYMLINK_NAME" echo "" echo "✅ Installation complete!" echo "--------------------------------------------------" echo " Executable: $INSTALLED_EXEC" echo " Symlink: /usr/local/bin/$SYMLINK_NAME" echo " Config: $DEST_CONFIG_PATH" echo "--------------------------------------------------" if [ "$CONFIG_STRATEGY" == "copy_generated" ]; then echo "⚠️ IMPORTANT: A default configuration file was created." echo " You MUST update it with your API Key before running:" echo " sudo nano $DEST_CONFIG_PATH" echo "" echo " Get your key here: https://aistudio.google.com/apikey" elif [ "$CONFIG_STRATEGY" == "restore" ]; then echo " Your previous configuration was preserved." fi echo "" echo "Run the client by typing: $SYMLINK_NAME"