This commit is contained in:
2025-12-03 21:31:30 +08:00
parent 3996c4fc2f
commit e5fa9429ae
6 changed files with 1481 additions and 12 deletions

View File

@@ -48,14 +48,30 @@ fi
# 检测系统类型和架构
detect_system() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
else
echo -e "${RED}无法检测系统类型${NC}"
exit 1
fi
# 检测操作系统类型linux/darwin/windows
OS_TYPE=$(uname -s | tr '[:upper:]' '[:lower:]')
case $OS_TYPE in
linux)
OS_TYPE="linux"
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
else
OS="linux"
OS_VERSION=""
fi
;;
darwin)
OS_TYPE="darwin"
OS="darwin"
OS_VERSION=$(sw_vers -productVersion 2>/dev/null || echo "")
;;
*)
echo -e "${RED}不支持的操作系统: $OS_TYPE${NC}"
exit 1
;;
esac
ARCH=$(uname -m)
case $ARCH in
@@ -71,7 +87,7 @@ detect_system() {
;;
esac
echo -e "${BLUE}检测到系统: $OS $OS_VERSION ($ARCH)${NC}"
echo -e "${BLUE}检测到系统: $OS_TYPE $OS_VERSION ($ARCH)${NC}"
}
# 检测并选择最快的镜像源
@@ -628,6 +644,256 @@ uninstall_service() {
echo ""
}
# 尝试从 Releases 下载二进制文件
download_binary_from_releases() {
echo -e "${BLUE}尝试从 Releases 下载预编译二进制文件...${NC}"
# Gitea API 地址
local api_base="https://gitee.nas.cpolar.cn/api/v1"
local repo_api="${api_base}/repos/${GITHUB_REPO}"
# 获取所有 releases按创建时间排序最新的在前
echo -e "${BLUE}获取最新 release 信息...${NC}"
local releases_response=$(curl -s -X GET "${repo_api}/releases?limit=10" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$releases_response" ]; then
echo -e "${YELLOW}⚠ 无法获取 releases 信息,将使用源码编译${NC}"
return 1
fi
# 解析所有 releases找到最新的按创建时间或版本号
# Gitea API 返回的 releases 通常已经按创建时间倒序排列
# 但我们还是需要解析并验证
# 提取所有 tag 和对应的 release_id、创建时间
local latest_tag=""
local latest_release_id=""
local latest_created_at=""
# 使用更健壮的方式解析 JSON虽然简单但能工作
# 查找第一个有效的 release非 draft非 prerelease
local tag_line=$(echo "$releases_response" | grep -o '"tag_name":"[^"]*"' | head -1)
local id_line=$(echo "$releases_response" | grep -o '"id":[0-9]*' | head -1)
local created_line=$(echo "$releases_response" | grep -o '"created_at":"[^"]*"' | head -1)
local draft_line=$(echo "$releases_response" | grep -o '"draft":[^,}]*' | head -1)
local prerelease_line=$(echo "$releases_response" | grep -o '"prerelease":[^,}]*' | head -1)
# 检查是否是 draft 或 prerelease
if echo "$draft_line" | grep -q "true" || echo "$prerelease_line" | grep -q "true"; then
# 如果是 draft 或 prerelease尝试找下一个
echo -e "${YELLOW}⚠ 第一个 release 是草稿或预发布版本,查找正式版本...${NC}"
# 简化处理:如果第一个是预发布,仍然使用它(因为可能是最新的)
fi
latest_tag=$(echo "$tag_line" | cut -d'"' -f4)
latest_release_id=$(echo "$id_line" | cut -d':' -f2)
latest_created_at=$(echo "$created_line" | cut -d'"' -f4)
if [ -z "$latest_tag" ] || [ -z "$latest_release_id" ]; then
echo -e "${YELLOW}⚠ 无法解析 release 信息,将使用源码编译${NC}"
return 1
fi
# 显示找到的版本信息
echo -e "${GREEN}✓ 找到最新版本: ${latest_tag}${NC}"
if [ -n "$latest_created_at" ]; then
echo -e "${BLUE} 发布日期: ${latest_created_at}${NC}"
fi
# 构建文件名(根据系统类型)
local file_name="agent-${OS_TYPE}-${ARCH}-${latest_tag}"
local file_ext="tar.gz"
if [ "$OS_TYPE" = "windows" ]; then
file_ext="zip"
fi
local full_file_name="${file_name}.${file_ext}"
# 获取 release 的详细信息(包含 commit hash
local release_detail=$(curl -s -X GET "${repo_api}/releases/${latest_release_id}" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$release_detail" ]; then
echo -e "${YELLOW}⚠ 无法获取 release 详细信息,将使用源码编译${NC}"
return 1
fi
# 解析 release 对应的 commit hashtarget_commitish
local release_commit=$(echo "$release_detail" | grep -o '"target_commitish":"[^"]*"' | head -1 | cut -d'"' -f4)
# 如果 release_detail 中没有 target_commitish尝试通过 tag 获取 commit hash
if [ -z "$release_commit" ]; then
# 通过 tag API 获取 commit hash
local tag_info=$(curl -s -X GET "${repo_api}/git/tags/${latest_tag}" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$tag_info" ]; then
release_commit=$(echo "$tag_info" | grep -o '"sha":"[^"]*"' | head -1 | cut -d'"' -f4)
fi
fi
# 如果还是无法获取 commit hash使用 assets_response 继续(不强制要求 commit
local assets_response="$release_detail"
# 查找匹配的二进制文件
local download_url=$(echo "$assets_response" | grep -o "\"browser_download_url\":\"[^\"]*${full_file_name}[^\"]*\"" | head -1 | cut -d'"' -f4)
if [ -z "$download_url" ]; then
echo -e "${YELLOW}⚠ 未找到匹配的二进制文件: ${full_file_name}${NC}"
echo -e "${YELLOW} 将使用源码编译${NC}"
return 1
fi
echo -e "${BLUE}下载二进制文件: ${full_file_name}...${NC}"
# 创建临时目录
local temp_dir=$(mktemp -d)
local download_file="${temp_dir}/${full_file_name}"
# 下载文件
if ! curl -fsSL -o "$download_file" "$download_url" 2>/dev/null; then
echo -e "${YELLOW}⚠ 下载失败,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
# 检查文件大小(至少应该大于 1MB
local file_size=$(stat -f%z "$download_file" 2>/dev/null || stat -c%s "$download_file" 2>/dev/null || echo "0")
if [ "$file_size" -lt 1048576 ]; then
echo -e "${YELLOW}⚠ 下载的文件大小异常,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
# 解压文件
echo -e "${BLUE}解压二进制文件...${NC}"
cd "$temp_dir"
if [ "$file_ext" = "tar.gz" ]; then
if ! tar -xzf "$download_file" 2>/dev/null; then
echo -e "${YELLOW}⚠ 解压失败,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
# 查找解压后的二进制文件(优先查找 agent然后是 agent-*
local binary_file=""
if [ -f "./agent" ] && [ -x "./agent" ]; then
binary_file="./agent"
else
binary_file=$(find . -maxdepth 1 -type f \( -name "agent" -o -name "agent-*" -o -name "Agent" -o -name "Agent-*" \) ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | head -1)
fi
else
# Windows zip 文件(虽然脚本主要在 Linux 上运行,但保留兼容性)
if ! unzip -q "$download_file" 2>/dev/null; then
echo -e "${YELLOW}⚠ 解压失败,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
local binary_file=$(find . -maxdepth 1 -type f \( -name "agent*.exe" -o -name "Agent*.exe" \) 2>/dev/null | head -1)
fi
if [ -z "$binary_file" ] || [ ! -f "$binary_file" ]; then
echo -e "${YELLOW}⚠ 未找到解压后的二进制文件,将使用源码编译${NC}"
echo -e "${YELLOW} 解压目录内容:${NC}"
ls -la "$temp_dir" 2>/dev/null || true
rm -rf "$temp_dir"
return 1
fi
# 验证二进制文件是否可执行
if [ ! -x "$binary_file" ]; then
chmod +x "$binary_file" 2>/dev/null || true
fi
# 验证二进制文件类型Linux 应该是 ELF 文件)
if [ "$OS_TYPE" = "linux" ]; then
if command -v file > /dev/null 2>&1; then
local file_type=$(file "$binary_file" 2>/dev/null || echo "")
if [ -n "$file_type" ] && ! echo "$file_type" | grep -qi "ELF"; then
echo -e "${YELLOW}⚠ 二进制文件类型异常: ${file_type}${NC}"
echo -e "${YELLOW} 将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
fi
fi
# 保存下载的二进制文件到临时位置
local downloaded_binary="${temp_dir}/downloaded_agent"
sudo cp "$binary_file" "$downloaded_binary"
sudo chmod +x "$downloaded_binary"
# 验证复制后的文件
if [ ! -f "$downloaded_binary" ] || [ ! -x "$downloaded_binary" ]; then
echo -e "${YELLOW}⚠ 二进制文件验证失败,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
# 清理临时下载文件
rm -f "$download_file"
# 克隆仓库(用于获取 run.sh 和 start-systemd.sh 等脚本)
echo -e "${BLUE}克隆仓库以获取启动脚本...${NC}"
if [ -d "$SOURCE_DIR" ]; then
sudo rm -rf "$SOURCE_DIR"
fi
if ! sudo git clone --branch "${GITHUB_BRANCH}" "https://gitee.nas.cpolar.cn/${GITHUB_REPO}.git" "$SOURCE_DIR" 2>&1; then
echo -e "${YELLOW}⚠ 克隆仓库失败,将使用源码编译${NC}"
rm -rf "$temp_dir"
return 1
fi
# 对比 Git commit hash
if [ -n "$release_commit" ]; then
echo -e "${BLUE}验证 Git commit 版本...${NC}"
cd "$SOURCE_DIR"
# 获取当前分支的最新 commit hash
local current_commit=$(git rev-parse HEAD 2>/dev/null || echo "")
if [ -n "$current_commit" ]; then
# 截取短 commit hash 用于显示前7位
local release_commit_short=$(echo "$release_commit" | cut -c1-7)
local current_commit_short=$(echo "$current_commit" | cut -c1-7)
echo -e "${BLUE} Release commit: ${release_commit_short}${NC}"
echo -e "${BLUE} 当前代码 commit: ${current_commit_short}${NC}"
# 对比 commit hash
if [ "$release_commit" != "$current_commit" ]; then
echo -e "${YELLOW}⚠ Commit hash 不匹配,二进制文件可能不是最新代码编译的${NC}"
echo -e "${YELLOW} Release 基于较旧的代码,将使用源码编译最新版本${NC}"
rm -rf "$temp_dir"
return 1
else
echo -e "${GREEN}✓ Commit hash 匹配,二进制文件是最新代码编译的${NC}"
fi
else
echo -e "${YELLOW}⚠ 无法获取当前代码的 commit hash跳过验证${NC}"
fi
else
echo -e "${YELLOW}⚠ 无法获取 release 的 commit hash跳过验证${NC}"
fi
# 用下载的二进制文件覆盖克隆目录中的文件
sudo cp "$downloaded_binary" "$SOURCE_DIR/agent"
sudo chmod +x "$SOURCE_DIR/agent"
# 复制到安装目录
sudo mkdir -p "$INSTALL_DIR"
sudo cp "$SOURCE_DIR/agent" "$INSTALL_DIR/$BINARY_NAME"
sudo chmod +x "$INSTALL_DIR/$BINARY_NAME"
# 清理临时文件
rm -rf "$temp_dir"
# 显示文件信息
local binary_size=$(du -h "$SOURCE_DIR/agent" | cut -f1)
echo -e "${GREEN}✓ 二进制文件下载完成 (文件大小: ${binary_size})${NC}"
echo -e "${BLUE}版本: ${latest_tag}${NC}"
echo -e "${BLUE}二进制文件: ${SOURCE_DIR}/agent${NC}"
return 0
}
# 从源码编译安装
build_from_source() {
echo -e "${BLUE}从源码编译安装节点端...${NC}"
@@ -779,6 +1045,22 @@ create_service() {
sudo chmod +x "$SOURCE_DIR/run.sh"
sudo chmod +x "$SOURCE_DIR/start-systemd.sh"
# 检测 Go 的安装路径
GO_PATH=""
if [ -d "/usr/local/go/bin" ]; then
GO_PATH="/usr/local/go/bin"
elif command -v go > /dev/null 2>&1; then
GO_BIN=$(command -v go)
GO_PATH=$(dirname "$GO_BIN")
fi
# 构建 PATH 环境变量
if [ -n "$GO_PATH" ]; then
ENV_PATH="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${GO_PATH}"
else
ENV_PATH="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
fi
sudo tee /etc/systemd/system/${SERVICE_NAME}.service > /dev/null <<EOF
[Unit]
Description=LinkMaster Node Service
@@ -792,6 +1074,7 @@ ExecStart=$SOURCE_DIR/start-systemd.sh
Restart=always
RestartSec=5
Environment="BACKEND_URL=$BACKEND_URL"
Environment="$ENV_PATH"
[Install]
WantedBy=multi-user.target
@@ -1032,7 +1315,15 @@ main() {
detect_fastest_mirror
install_dependencies
build_from_source
# 优先尝试从 Releases 下载二进制文件
if ! download_binary_from_releases; then
echo -e "${BLUE}从 Releases 下载失败,开始从源码编译...${NC}"
build_from_source
else
echo -e "${GREEN}✓ 使用预编译二进制文件,跳过编译步骤${NC}"
fi
create_service
configure_firewall
register_node