#!/bin/bash set -e # ============================================================================ # Configuration # ============================================================================ GITHUB_REPO="Gouryella/drip" INSTALL_DIR="${INSTALL_DIR:-}" VERSION="${VERSION:-}" BINARY_NAME="drip" UNINSTALL_MODE=false COMMAND_MADE_AVAILABLE=false # 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' # Language (default: en) LANG_CODE="${LANG_CODE:-en}" # ============================================================================ # Internationalization # ============================================================================ msg_en() { case "$1" in banner_title) echo "Drip Client - One-Click Installer" ;; select_lang) echo "Select language / 选择语言" ;; lang_en) echo "English" ;; lang_zh) echo "中文" ;; checking_os) echo "Checking operating system..." ;; detected_os) echo "Detected OS" ;; unsupported_os) echo "Unsupported operating system" ;; checking_arch) echo "Checking system architecture..." ;; detected_arch) echo "Detected architecture" ;; unsupported_arch) echo "Unsupported architecture" ;; checking_deps) echo "Checking dependencies..." ;; deps_ok) echo "Dependencies check passed" ;; downloading) echo "Downloading Drip client" ;; download_failed) echo "Download failed" ;; download_ok) echo "Download completed" ;; select_install_dir) echo "Select installation directory" ;; option_user) echo "User directory (no sudo required)" ;; option_system) echo "System directory (requires sudo)" ;; option_current) echo "Current directory" ;; option_custom) echo "Custom path" ;; enter_custom_path) echo "Enter custom path" ;; installing) echo "Installing binary..." ;; install_ok) echo "Installation completed" ;; updating_path) echo "Updating PATH..." ;; path_updated) echo "PATH updated" ;; path_note) echo "Please restart your terminal or run: source ~/.bashrc" ;; config_title) echo "Client Configuration" ;; configure_now) echo "Configure client now?" ;; enter_server) echo "Enter server address (e.g., tunnel.example.com:8443)" ;; server_required) echo "Server address is required" ;; enter_token) echo "Enter authentication token" ;; token_required) echo "Token is required" ;; skip_verify) echo "Skip TLS certificate verification? (for self-signed certs)" ;; config_saved) echo "Configuration saved" ;; install_complete) echo "Installation completed!" ;; usage_title) echo "Usage" ;; usage_http) echo "Expose HTTP service on port 3000" ;; usage_tcp) echo "Expose TCP service on port 5432" ;; usage_config) echo "Show/modify configuration" ;; usage_daemon) echo "Run as background daemon" ;; run_test) echo "Test connection now?" ;; test_running) echo "Testing connection..." ;; test_success) echo "Connection successful" ;; test_failed) echo "Connection failed" ;; yes) echo "y" ;; no) echo "n" ;; press_enter) echo "Press Enter to continue..." ;; windows_note) echo "For Windows, please download the .exe file from GitHub Releases" ;; already_installed) echo "Drip is already installed" ;; current_version) echo "Current version" ;; update_now) echo "Update to the latest version?" ;; updating) echo "Updating..." ;; update_ok) echo "Update completed" ;; verify_install) echo "Verifying installation..." ;; verify_ok) echo "Verification passed" ;; verify_failed) echo "Verification failed" ;; insecure_note) echo "Only use --insecure for development/testing" ;; uninstall_title) echo "Drip Client - Uninstall" ;; uninstall_prompt) echo "Uninstall Drip client now?" ;; uninstalling) echo "Uninstalling Drip client..." ;; uninstall_done) echo "Uninstall completed" ;; uninstall_not_found) echo "Drip is not installed" ;; remove_config_prompt) echo "Remove Drip config directory as well?" ;; config_removed) echo "Config removed" ;; path_cleanup) echo "Cleaning PATH entries..." ;; path_cleanup_done) echo "PATH entries cleaned" ;; *) echo "$1" ;; esac } msg_zh() { case "$1" in banner_title) echo "Drip 客户端 - 一键安装脚本" ;; select_lang) echo "Select language / 选择语言" ;; lang_en) echo "English" ;; lang_zh) echo "中文" ;; checking_os) echo "检查操作系统..." ;; detected_os) echo "检测到操作系统" ;; unsupported_os) echo "不支持的操作系统" ;; checking_arch) echo "检查系统架构..." ;; detected_arch) echo "检测到架构" ;; unsupported_arch) echo "不支持的架构" ;; checking_deps) echo "检查依赖..." ;; deps_ok) echo "依赖检查通过" ;; downloading) echo "下载 Drip 客户端" ;; download_failed) echo "下载失败" ;; download_ok) echo "下载完成" ;; select_install_dir) echo "选择安装目录" ;; option_user) echo "用户目录(无需 sudo)" ;; option_system) echo "系统目录(需要 sudo)" ;; option_current) echo "当前目录" ;; option_custom) echo "自定义路径" ;; enter_custom_path) echo "输入自定义路径" ;; installing) echo "安装二进制文件..." ;; install_ok) echo "安装完成" ;; updating_path) echo "更新 PATH..." ;; path_updated) echo "PATH 已更新" ;; path_note) echo "请重启终端或运行: source ~/.bashrc" ;; config_title) echo "客户端配置" ;; configure_now) echo "现在配置客户端?" ;; enter_server) echo "输入服务器地址(例如:tunnel.example.com:8443)" ;; server_required) echo "服务器地址是必填项" ;; enter_token) echo "输入认证令牌" ;; token_required) echo "认证令牌是必填项" ;; skip_verify) echo "跳过 TLS 证书验证?(用于自签名证书)" ;; config_saved) echo "配置已保存" ;; install_complete) echo "安装完成!" ;; usage_title) echo "使用方法" ;; usage_http) echo "暴露本地 3000 端口的 HTTP 服务" ;; usage_tcp) echo "暴露本地 5432 端口的 TCP 服务" ;; usage_config) echo "显示/修改配置" ;; usage_daemon) echo "作为后台守护进程运行" ;; run_test) echo "现在测试连接?" ;; test_running) echo "正在测试连接..." ;; test_success) echo "连接成功" ;; test_failed) echo "连接失败" ;; yes) echo "y" ;; no) echo "n" ;; press_enter) echo "按 Enter 继续..." ;; windows_note) echo "Windows 用户请从 GitHub Releases 下载 .exe 文件" ;; already_installed) echo "Drip 已安装" ;; current_version) echo "当前版本" ;; update_now) echo "是否更新到最新版本?" ;; updating) echo "正在更新..." ;; update_ok) echo "更新完成" ;; verify_install) echo "验证安装..." ;; verify_ok) echo "验证通过" ;; verify_failed) echo "验证失败" ;; insecure_note) echo "--insecure 仅用于开发/测试环境" ;; uninstall_title) echo "Drip 客户端 - 卸载" ;; uninstall_prompt) echo "现在卸载 Drip 客户端?" ;; uninstalling) echo "正在卸载 Drip 客户端..." ;; uninstall_done) echo "卸载完成" ;; uninstall_not_found) echo "未检测到已安装的 Drip" ;; remove_config_prompt) echo "是否同时删除 Drip 配置目录?" ;; config_removed) echo "配置已删除" ;; path_cleanup) echo "清理 PATH 相关配置..." ;; path_cleanup_done) echo "PATH 配置已清理" ;; *) echo "$1" ;; esac } # Get message by key (bash 3.2 compatible) msg() { local key="$1" if [[ "$LANG_CODE" == "zh" ]]; then msg_zh "$key" else msg_en "$key" fi } # Prompt helper compatible with bash 3.2 and zsh prompt_input() { local __prompt="$1" local __var_name="$2" printf "%s" "$__prompt" IFS= read -r "$__var_name" < /dev/tty } # ============================================================================ # Output functions # ============================================================================ print_info() { echo -e "${BLUE}[INFO]${NC} $1"; } print_success() { echo -e "${GREEN}[✓]${NC} $1"; } print_warning() { echo -e "${YELLOW}[!]${NC} $1"; } print_error() { echo -e "${RED}[✗]${NC} $1"; } print_step() { echo -e "${CYAN}[→]${NC} $1"; } repeat_char() { local char="$1" local count="$2" local out="" for _ in $(seq 1 "$count"); do out+="$char" done echo "$out" } print_panel() { local title="$1" shift local width=58 local bar bar=$(repeat_char "=" "$width") echo "" echo -e "${CYAN}${bar}${NC}" echo -e "${CYAN}${title}${NC}" echo -e "${CYAN}${bar}${NC}" for line in "$@"; do echo -e " $line" done echo -e "${CYAN}${bar}${NC}" echo "" } print_subheader() { local title="$1" local width=58 local bar bar=$(repeat_char "-" "$width") echo "" echo -e "${CYAN}${title}${NC}" echo -e "${CYAN}${bar}${NC}" } # Print banner print_banner() { echo -e "${GREEN}" cat << "EOF" ____ _ / __ \_____(_)___ / / / / ___/ / __ \ / /_/ / / / / /_/ / /_____/_/ /_/ .___/ /_/ EOF echo -e "${BOLD}$(msg banner_title)${NC}" echo "" } # Extract version from a drip binary, preferring the plain output when available get_version_from_binary() { local binary="$1" local output="" local version="" output=$("$binary" version --short 2>/dev/null || true) if [[ -n "$output" ]]; then version=$(printf '%s\n' "$output" | awk -F': ' '/Version/ {print $2; exit}') fi if [[ -z "$version" ]]; then output=$("$binary" version 2>/dev/null || true) if [[ -n "$output" ]]; then output=$(printf '%s\n' "$output" | sed -E $'s/\x1b\\[[0-9;]*[A-Za-z]//g') version=$(printf '%s\n' "$output" | sed -nE 's/.*Version:[[:space:]]*([vV]?[0-9][^[:space:]]*).*/\1/p' | head -n1) fi fi echo "${version:-unknown}" } # ============================================================================ # Language selection # ============================================================================ select_language() { print_panel "$(msg select_lang)" \ "${GREEN}1)${NC} English" \ "${GREEN}2)${NC} 中文" prompt_input "Select [1]: " lang_choice case "$lang_choice" in 2) LANG_CODE="zh" ;; *) LANG_CODE="en" ;; esac echo "" } # ============================================================================ # System checks # ============================================================================ check_os() { print_step "$(msg checking_os)" case "$(uname -s)" in Linux*) OS="linux" ;; Darwin*) OS="darwin" ;; MINGW*|MSYS*|CYGWIN*) OS="windows" print_warning "$(msg windows_note)" ;; *) print_error "$(msg unsupported_os): $(uname -s)" exit 1 ;; esac print_success "$(msg detected_os): $OS" } check_arch() { print_step "$(msg checking_arch)" case "$(uname -m)" in x86_64|amd64) ARCH="amd64" ;; aarch64|arm64) ARCH="arm64" ;; armv7l) ARCH="armv7" ;; armv6l) ARCH="armv6" ;; i386|i686) ARCH="386" ;; *) print_error "$(msg unsupported_arch): $(uname -m)" exit 1 ;; esac print_success "$(msg detected_arch): $ARCH" } check_dependencies() { print_step "$(msg checking_deps)" # Check for download tool if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then print_error "curl or wget is required" exit 1 fi print_success "$(msg deps_ok)" } get_latest_version() { # Get latest version from GitHub API local api_url="https://api.github.com/repos/${GITHUB_REPO}/releases/latest" local version="" if command -v curl &> /dev/null; then version=$(curl -fsSL "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/' 2>/dev/null) else version=$(wget -qO- "$api_url" | grep '"tag_name":' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/' 2>/dev/null) fi if [[ -z "$version" ]]; then print_error "Failed to get latest version from GitHub" exit 1 fi echo "$version" } check_existing_install() { if command -v drip &> /dev/null; then local current_path=$(command -v drip) local current_version=$(get_version_from_binary "drip") print_warning "$(msg already_installed): $current_path" print_info "$(msg current_version): $current_version" # Check remote version print_step "Checking for updates..." local latest_version=$(get_latest_version) if [[ "$current_version" == "$latest_version" ]]; then print_success "Already up to date ($current_version)" exit 0 else print_info "Latest version: $latest_version" echo "" prompt_input "$(msg update_now) [Y/n]: " update_choice fi if [[ "$update_choice" =~ ^[Nn]$ ]]; then exit 0 fi INSTALL_DIR=$(dirname "$current_path") IS_UPDATE=true fi } # ============================================================================ # Download and install # ============================================================================ get_download_url() { # Get latest version if not set if [[ -z "$VERSION" ]]; then VERSION=$(get_latest_version) fi # Strip 'v' prefix for archive filename (v0.7.0 -> 0.7.0) local version_number="${VERSION#v}" local archive_name local ext="tar.gz" if [[ "$OS" == "windows" ]]; then archive_name="drip_${version_number}_windows_${ARCH}.${ext}" else archive_name="drip_${version_number}_${OS}_${ARCH}.${ext}" fi echo "https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${archive_name}" } download_binary() { local url=$(get_download_url) if [[ "$IS_UPDATE" == true ]]; then print_step "$(msg updating)..." else print_step "$(msg downloading)..." fi local tmp_archive="/tmp/drip-archive.tar.gz" local tmp_dir="/tmp/drip-extract" # Clean up any previous extraction rm -rf "$tmp_dir" mkdir -p "$tmp_dir" if command -v curl &> /dev/null; then # Use -# for progress bar instead of -s (silent) if ! curl -f#L "$url" -o "$tmp_archive"; then print_error "$(msg download_failed): $url" exit 1 fi else # Use --show-progress to display download progress if ! wget --show-progress "$url" -O "$tmp_archive" 2>&1 | grep -v "^$"; then print_error "$(msg download_failed): $url" exit 1 fi fi # Extract the archive if ! tar -xzf "$tmp_archive" -C "$tmp_dir"; then print_error "Failed to extract archive" exit 1 fi # Find the binary (it should be named 'drip' or 'drip.exe') local extracted_binary="" if [[ "$OS" == "windows" ]]; then extracted_binary=$(find "$tmp_dir" -name "drip.exe" -type f 2>/dev/null | head -1) else extracted_binary=$(find "$tmp_dir" -name "drip" -type f 2>/dev/null | head -1) fi if [[ -z "$extracted_binary" ]]; then print_error "Binary not found in archive" exit 1 fi # Move to standard location mv "$extracted_binary" /tmp/drip-download chmod +x /tmp/drip-download # Clean up rm -rf "$tmp_archive" "$tmp_dir" print_success "$(msg download_ok)" } select_install_dir() { if [[ -n "$INSTALL_DIR" ]]; then return fi print_panel "$(msg select_install_dir)" \ "${GREEN}1)${NC} ~/.local/bin $(msg option_user)" \ "${GREEN}2)${NC} /usr/local/bin $(msg option_system)" \ "${GREEN}3)${NC} ./ $(msg option_current)" \ "${GREEN}4)${NC} $(msg option_custom)" prompt_input "Select [1]: " dir_choice case "$dir_choice" in 2) INSTALL_DIR="/usr/local/bin" NEED_SUDO=true ;; 3) INSTALL_DIR="." ;; 4) prompt_input "$(msg enter_custom_path): " INSTALL_DIR ;; *) INSTALL_DIR="$HOME/.local/bin" ;; esac } install_binary() { print_step "$(msg installing)" # Create directory if needed if [[ ! -d "$INSTALL_DIR" ]]; then if [[ "$NEED_SUDO" == true ]]; then sudo mkdir -p "$INSTALL_DIR" else mkdir -p "$INSTALL_DIR" fi fi local target_path="$INSTALL_DIR/$BINARY_NAME" if [[ "$OS" == "windows" ]]; then target_path="$INSTALL_DIR/$BINARY_NAME.exe" fi # Install binary if [[ "$NEED_SUDO" == true ]]; then sudo mv /tmp/drip-download "$target_path" sudo chmod +x "$target_path" else mv /tmp/drip-download "$target_path" chmod +x "$target_path" fi print_success "$(msg install_ok): $target_path" } update_path() { # Skip if already in PATH if command -v drip &> /dev/null; then return fi if [[ "$COMMAND_MADE_AVAILABLE" == true ]]; then return fi # Skip for system directories (usually already in PATH) if [[ "$INSTALL_DIR" == "/usr/local/bin" ]] || [[ "$INSTALL_DIR" == "/usr/bin" ]]; then return fi print_step "$(msg updating_path)" local shell_rc="" local export_line="export PATH=\"\$PATH:$INSTALL_DIR\"" # Determine shell config file if [[ -n "$ZSH_VERSION" ]] || [[ "$SHELL" == *"zsh"* ]]; then shell_rc="$HOME/.zshrc" elif [[ -n "$BASH_VERSION" ]] || [[ "$SHELL" == *"bash"* ]]; then if [[ "$OS" == "darwin" ]]; then shell_rc="$HOME/.bash_profile" else shell_rc="$HOME/.bashrc" fi elif [[ "$SHELL" == *"fish"* ]]; then shell_rc="$HOME/.config/fish/config.fish" export_line="set -gx PATH \$PATH $INSTALL_DIR" fi if [[ -n "$shell_rc" ]]; then # Check if already added if ! grep -q "$INSTALL_DIR" "$shell_rc" 2>/dev/null; then echo "" >> "$shell_rc" echo "# Drip client" >> "$shell_rc" echo "$export_line" >> "$shell_rc" print_success "$(msg path_updated): $shell_rc" fi fi print_warning "$(msg path_note)" } ensure_command_access() { # Try to make the command available immediately without requiring shell restart if [[ "$OS" == "windows" ]]; then return fi hash -r 2>/dev/null || true command -v rehash >/dev/null 2>&1 && rehash || true if command -v "$BINARY_NAME" >/dev/null 2>&1; then COMMAND_MADE_AVAILABLE=true return fi local target_path="$INSTALL_DIR/$BINARY_NAME" local preferred="/usr/local/bin" if [[ ":$PATH:" == *":$preferred:"* ]]; then if [[ -w "$preferred" ]]; then if ln -sf "$target_path" "$preferred/$BINARY_NAME" 2>/dev/null; then COMMAND_MADE_AVAILABLE=true print_success "Made ${BINARY_NAME} available at $preferred/$BINARY_NAME" fi else if sudo ln -sf "$target_path" "$preferred/$BINARY_NAME" 2>/dev/null; then COMMAND_MADE_AVAILABLE=true print_success "Made ${BINARY_NAME} available at $preferred/$BINARY_NAME" fi fi fi # Refresh hash table hash -r 2>/dev/null || true command -v rehash >/dev/null 2>&1 && rehash || true } verify_installation() { print_step "$(msg verify_install)" local binary_path="$INSTALL_DIR/$BINARY_NAME" if [[ "$OS" == "windows" ]]; then binary_path="$INSTALL_DIR/$BINARY_NAME.exe" fi if [[ -x "$binary_path" ]]; then local version=$(get_version_from_binary "$binary_path") print_success "$(msg verify_ok): $version" else print_error "$(msg verify_failed)" exit 1 fi } # ============================================================================ # Configuration # ============================================================================ configure_client() { echo "" prompt_input "$(msg configure_now) [Y/n]: " config_choice if [[ "$config_choice" =~ ^[Nn]$ ]]; then return fi print_subheader "$(msg config_title)" local binary_path="$INSTALL_DIR/$BINARY_NAME" # Server address while true; do prompt_input "$(msg enter_server): " SERVER if [[ -n "$SERVER" ]]; then break fi print_error "$(msg server_required)" done # Token while true; do prompt_input "$(msg enter_token): " TOKEN if [[ -n "$TOKEN" ]]; then break fi print_error "$(msg token_required)" done # Insecure mode prompt_input "$(msg skip_verify) [y/N]: " insecure_choice INSECURE="" if [[ "$insecure_choice" =~ ^[Yy]$ ]]; then INSECURE="--insecure" print_warning "$(msg insecure_note)" fi # Save configuration "$binary_path" config set --server "$SERVER" --token "$TOKEN" $INSECURE 2>/dev/null || true print_success "$(msg config_saved)" } # ============================================================================ # Test connection # ============================================================================ test_connection() { echo "" prompt_input "$(msg run_test) [y/N]: " test_choice if [[ ! "$test_choice" =~ ^[Yy]$ ]]; then return fi print_step "$(msg test_running)" local binary_path="$INSTALL_DIR/$BINARY_NAME" # Try to validate config if "$binary_path" config validate 2>/dev/null; then print_success "$(msg test_success)" else print_warning "$(msg test_failed)" fi } # ============================================================================ # Final output # ============================================================================ show_completion() { local binary_path="$INSTALL_DIR/$BINARY_NAME" print_panel "$(msg install_complete)" echo -e "${CYAN}$(msg usage_title):${NC}" echo "" echo -e " ${GREEN}# $(msg usage_http)${NC}" echo -e " ${YELLOW}$BINARY_NAME http 3000${NC}" echo "" echo -e " ${GREEN}# $(msg usage_tcp)${NC}" echo -e " ${YELLOW}$BINARY_NAME tcp 5432${NC}" echo "" echo -e " ${GREEN}# $(msg usage_config)${NC}" echo -e " ${YELLOW}$BINARY_NAME config show${NC}" echo -e " ${YELLOW}$BINARY_NAME config init${NC}" echo "" echo -e " ${GREEN}# $(msg usage_daemon)${NC}" echo -e " ${YELLOW}$BINARY_NAME daemon start http 3000${NC}" echo -e " ${YELLOW}$BINARY_NAME daemon list${NC}" echo "" } # ============================================================================ # Uninstall # ============================================================================ cleanup_path_entries() { local install_dir="$1" local candidates=() local os_name="$OS" if [[ -z "$os_name" ]]; then case "$(uname -s)" in Darwin*) os_name="darwin" ;; *) os_name="linux" ;; esac fi # Determine shell config files if [[ -n "$ZSH_VERSION" ]] || [[ "$SHELL" == *"zsh"* ]]; then candidates+=("$HOME/.zshrc") fi if [[ -n "$BASH_VERSION" ]] || [[ "$SHELL" == *"bash"* ]]; then if [[ "$os_name" == "darwin" ]]; then candidates+=("$HOME/.bash_profile") else candidates+=("$HOME/.bashrc") fi fi if [[ "$SHELL" == *"fish"* ]]; then candidates+=("$HOME/.config/fish/config.fish") fi for file in "${candidates[@]}"; do if [[ -f "$file" ]] && grep -q "$install_dir" "$file" 2>/dev/null; then local tmp tmp=$(mktemp) # Remove the comment marker and PATH export line we added grep -v "Drip client" "$file" | grep -v "$install_dir" > "$tmp" || true mv "$tmp" "$file" fi done } remove_config_dirs() { local removed=false local dirs=("$HOME/.drip" "$HOME/.config/drip") echo "" prompt_input "$(msg remove_config_prompt) [y/N]: " remove_choice if [[ ! "$remove_choice" =~ ^[Yy]$ ]]; then return fi for dir in "${dirs[@]}"; do if [[ -d "$dir" ]]; then rm -rf "$dir" removed=true fi done if [[ "$removed" == true ]]; then print_success "$(msg config_removed)" fi } uninstall_client() { if ! command -v drip &> /dev/null; then print_warning "$(msg uninstall_not_found)" return fi local current_path current_path=$(command -v drip) prompt_input "$(msg uninstall_prompt) [y/N]: " confirm_uninstall if [[ ! "$confirm_uninstall" =~ ^[Yy]$ ]]; then return fi print_step "$(msg uninstalling)" if [[ -w "$current_path" ]]; then rm -f "$current_path" || true else sudo rm -f "$current_path" || true fi print_success "$(msg uninstall_done)" local install_dir install_dir=$(dirname "$current_path") print_step "$(msg path_cleanup)" cleanup_path_entries "$install_dir" print_success "$(msg path_cleanup_done)" remove_config_dirs } # ============================================================================ # Main # ============================================================================ main() { if [[ "$1" == "--uninstall" || "$1" == "uninstall" ]]; then UNINSTALL_MODE=true fi clear print_banner if [[ "$SKIP_LANG_PROMPT" == "true" ]]; then # Respect pre-set LANG_CODE when skipping the language prompt LANG_CODE="${LANG_CODE:-en}" else select_language fi echo -e "${BOLD}────────────────────────────────────────────${NC}" if [[ "$UNINSTALL_MODE" == true ]]; then uninstall_client exit 0 fi check_os check_arch check_dependencies check_existing_install echo "" download_binary select_install_dir install_binary ensure_command_access update_path verify_installation # Skip configuration for updates if [[ "$IS_UPDATE" != true ]]; then configure_client test_connection else echo "" local new_version=$(get_version_from_binary "$INSTALL_DIR/$BINARY_NAME") print_panel "$(msg update_ok)" print_info "Version: $new_version" echo "" return fi show_completion } # Run main "$@"