#!/bin/bash set -e # Beszel Agent 更新脚本 # 支持 Debian/Ubuntu, Arch, RHEL/CentOS/Fedora 等 systemd 系统 SERVICE_NAME="beszel-agent.service" MIRROR_BASE_URL="https://index.mitsea.com/d/Github%20Release%20Mirrors/Beszel" MIRROR_API_URL="https://index.mitsea.com/api/fs/list" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1" >&2 } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2 } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } # 检查是否以 root 运行 check_root() { if [[ $EUID -ne 0 ]]; then log_error "此脚本需要 root 权限运行" log_info "请使用 sudo $0" exit 1 fi } # 检查依赖 check_dependencies() { local missing_deps=() for cmd in curl tar systemctl; do if ! command -v "$cmd" &> /dev/null; then missing_deps+=("$cmd") fi done if [[ ${#missing_deps[@]} -gt 0 ]]; then log_error "缺少依赖: ${missing_deps[*]}" log_info "请先安装这些依赖" exit 1 fi } # 版本号比较函数:返回 0 表示 $1 > $2,返回 1 表示 $1 <= $2 version_gt() { local v1="${1#v}" local v2="${2#v}" # 使用 sort -V 进行版本号比较 if [[ "$(printf '%s\n%s' "$v1" "$v2" | sort -V | tail -n1)" == "$v1" && "$v1" != "$v2" ]]; then return 0 else return 1 fi } # 从 API 获取最新版本号 get_latest_version() { log_info "正在从镜像站获取最新版本信息..." local response response=$(curl -s -X POST "$MIRROR_API_URL" \ -H 'Content-Type: application/json' \ -d '{"path": "/Github Release Mirrors/Beszel"}') if [[ -z "$response" ]]; then log_error "无法获取版本信息" exit 1 fi # 解析版本号列表 local versions if command -v jq &> /dev/null; then versions=$(echo "$response" | jq -r '.data.content[].name' 2>/dev/null) else # 使用 grep 和 sed 作为备选方案 versions=$(echo "$response" | grep -o '"name":"v[^"]*"' | sed 's/"name":"//g; s/"//g') fi if [[ -z "$versions" ]]; then log_error "无法解析版本列表" exit 1 fi # 找出最新版本(按版本号排序) local latest_version latest_version=$(echo "$versions" | sort -V | tail -n1) if [[ -z "$latest_version" ]]; then log_error "无法确定最新版本" exit 1 fi echo "$latest_version" } # 从 systemd service 文件获取二进制文件路径 get_binary_path() { local service_file="/etc/systemd/system/$SERVICE_NAME" if [[ ! -f "$service_file" ]]; then log_error "找不到 service 文件: $service_file" exit 1 fi local exec_start exec_start=$(grep "^ExecStart=" "$service_file" | cut -d'=' -f2-) if [[ -z "$exec_start" ]]; then log_error "无法从 service 文件中获取 ExecStart" exit 1 fi # 提取二进制文件路径(取第一个参数,即可执行文件路径) local binary_path binary_path=$(echo "$exec_start" | awk '{print $1}') echo "$binary_path" } # 获取当前版本 get_current_version() { local binary_path="$1" if [[ ! -f "$binary_path" ]]; then echo "unknown" return fi # beszel-agent 不支持 --version,尝试用 strings 从二进制中提取版本号 # 版本号格式通常是 x.y.z local version="" if command -v strings &> /dev/null; then version=$(strings "$binary_path" 2>/dev/null | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) fi # 如果 strings 不可用或没找到,尝试用 grep if [[ -z "$version" ]]; then version=$(grep -aoE '[0-9]+\.[0-9]+\.[0-9]+' "$binary_path" 2>/dev/null | head -n1) fi if [[ -z "$version" ]]; then echo "unknown" else echo "$version" fi } # 检测系统架构 detect_arch() { local arch arch=$(uname -m) case "$arch" in x86_64|amd64) echo "amd64" ;; aarch64|arm64) echo "arm64" ;; armv7l|armhf) echo "arm" ;; i386|i686) echo "386" ;; *) log_error "不支持的架构: $arch" exit 1 ;; esac } # 下载并更新 download_and_update() { local version="$1" local binary_path="$2" local arch="$3" # 构建下载 URL(文件名不含版本号) local filename="beszel-agent_linux_${arch}.tar.gz" local download_url="${MIRROR_BASE_URL}/${version}/${filename}" log_info "下载 URL: $download_url" # 创建临时目录 local tmp_dir tmp_dir=$(mktemp -d) trap "rm -rf $tmp_dir" EXIT local tar_file="$tmp_dir/$filename" # 下载文件 log_info "正在下载 $filename ..." if ! curl -L -f -o "$tar_file" "$download_url"; then log_error "下载失败" exit 1 fi # 解压 log_info "正在解压..." if ! tar -xzf "$tar_file" -C "$tmp_dir"; then log_error "解压失败" exit 1 fi # 查找解压后的二进制文件 local new_binary="$tmp_dir/beszel-agent" if [[ ! -f "$new_binary" ]]; then log_error "解压后找不到 beszel-agent 文件" exit 1 fi # 停止服务 log_info "正在停止 $SERVICE_NAME ..." systemctl stop "$SERVICE_NAME" || true # 备份旧文件 local backup_path="${binary_path}.bak" if [[ -f "$binary_path" ]]; then log_info "备份旧文件到 $backup_path" cp "$binary_path" "$backup_path" fi # 替换二进制文件 log_info "正在更新二进制文件..." cp "$new_binary" "$binary_path" chmod +x "$binary_path" # 启动服务 log_info "正在启动 $SERVICE_NAME ..." systemctl start "$SERVICE_NAME" # 检查服务状态 sleep 2 if systemctl is-active --quiet "$SERVICE_NAME"; then log_info "服务启动成功" else log_error "服务启动失败,正在回滚..." if [[ -f "$backup_path" ]]; then cp "$backup_path" "$binary_path" systemctl start "$SERVICE_NAME" fi exit 1 fi } # 主函数 main() { log_info "===== Beszel Agent 更新脚本 =====" check_root check_dependencies # 获取二进制文件路径 local binary_path binary_path=$(get_binary_path) log_info "二进制文件路径: $binary_path" # 获取当前版本 local current_version current_version=$(get_current_version "$binary_path") log_info "当前版本: $current_version" # 获取最新版本 local latest_version latest_version=$(get_latest_version) log_info "最新版本: $latest_version" # 检测架构 local arch arch=$(detect_arch) log_info "系统架构: $arch" # 比较版本(简单比较,如果当前版本包含最新版本号则跳过) local latest_version_num="${latest_version#v}" if [[ "$current_version" == *"$latest_version_num"* ]]; then log_info "已经是最新版本,无需更新" exit 0 fi # 确认更新 if [[ "$AUTO_CONFIRM" != "true" ]]; then read -p "是否更新到 $latest_version? [y/N] " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_info "取消更新" exit 0 fi fi # 执行更新 download_and_update "$latest_version" "$binary_path" "$arch" log_info "===== 更新完成 =====" log_info "已更新到: $latest_version" } # 解析参数 while [[ $# -gt 0 ]]; do case "$1" in -y|--yes) AUTO_CONFIRM="true" shift ;; -h|--help) echo "用法: $0 [选项]" echo "" echo "选项:" echo " -y, --yes 自动确认更新,不询问" echo " -h, --help 显示帮助信息" exit 0 ;; *) log_error "未知参数: $1" echo "使用 -h 或 --help 查看帮助" exit 1 ;; esac done main