From bb73e0f3848b0381df0aff85b6727a75169532ed Mon Sep 17 00:00:00 2001 From: yoyo Date: Wed, 24 Dec 2025 01:31:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=89=93=E5=8C=85?= =?UTF-8?q?=E5=92=8C=E5=AE=89=E8=A3=85=E9=80=BB=E8=BE=91=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=B0=E6=A0=BC=E5=BC=8F=E5=8F=91=E5=B8=83=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 all-upload-release.sh 中添加临时打包目录,复制二进制文件及必要的脚本和配置文件。 - 修改 install.sh 以支持新格式发布包的提取,简化安装流程,无需从 Git 克隆。 - 更新 INSTALL.md 和 README.md,说明新格式发布包的优点和安装步骤。 - 确保安装脚本能够处理旧格式发布包,保持向后兼容性。 --- INSTALL.md | 48 ++++++++-- README.md | 2 + all-upload-release.sh | 40 +++++++- config.yaml.example | 18 ++++ install.sh | 216 +++++++++++++++++++++++++++--------------- 5 files changed, 237 insertions(+), 87 deletions(-) create mode 100644 config.yaml.example diff --git a/INSTALL.md b/INSTALL.md index d05e67e..4605e65 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -37,13 +37,17 @@ GITHUB_BRANCH=develop curl -fsSL https://raw.githubusercontent.com/yourbask/link 1. **检测系统** - 自动识别 Linux 发行版和 CPU 架构 2. **安装依赖** - 自动安装 Git、Go、ping、traceroute、dnsutils 等工具 -3. **克隆源码** - 从 GitHub 克隆 node 项目源码到 `/opt/linkmaster-node` -4. **编译安装** - 自动编译源码并安装二进制文件 -5. **创建服务** - 自动创建 systemd 服务文件(使用 run.sh 启动) -6. **启动服务** - 自动启动并设置开机自启 -7. **验证安装** - 检查服务状态和健康检查 +3. **下载发布包** - 从 Releases 下载预编译发布包(包含二进制文件和所有脚本) +4. **提取文件** - 从发布包提取所有文件到 `/opt/linkmaster-node`(无需克隆 Git) +5. **编译安装** - 如果下载失败,自动从源码编译安装 +6. **创建服务** - 自动创建 systemd 服务文件(使用 run.sh 启动) +7. **启动服务** - 自动启动并设置开机自启 +8. **验证安装** - 检查服务状态和健康检查 -**注意:** 每次服务启动时会自动拉取最新代码并重新编译,确保使用最新版本。 +**重要说明:** +- ✅ **新格式发布包**:包含二进制文件、安装脚本、运行脚本等所有必要文件,安装时直接从压缩包提取,无需克隆 Git 仓库 +- ✅ **向后兼容**:如果发布包是旧格式(仅包含二进制文件),安装脚本会自动从 Git 克隆获取脚本文件 +- ⚠️ **源码编译模式**:如果下载失败,会从 Git 克隆源码并编译(需要网络连接) ## 安装后管理 @@ -109,7 +113,37 @@ curl http://localhost:2200/api/health 如果无法使用一键安装脚本,可以手动安装: -### 1. 克隆源码并编译 +### 方式一:从发布包安装(推荐) + +**优点**:无需 Git 和 Go 环境,直接使用预编译文件 + +```bash +# 1. 下载发布包(替换为实际版本和平台) +wget https://gitee.nas.cpolar.cn/yoyo/linkmaster-node/releases/download/v1.1.4/agent-linux-amd64-v1.1.4.tar.gz + +# 2. 解压 +tar -xzf agent-linux-amd64-v1.1.4.tar.gz +cd agent-linux-amd64-v1.1.4 + +# 3. 复制文件到安装目录 +sudo mkdir -p /opt/linkmaster-node +sudo cp -r * /opt/linkmaster-node/ +sudo chmod +x /opt/linkmaster-node/agent +sudo chmod +x /opt/linkmaster-node/*.sh + +# 4. 复制二进制文件到系统目录 +sudo cp /opt/linkmaster-node/agent /usr/local/bin/linkmaster-node +sudo chmod +x /usr/local/bin/linkmaster-node + +# 5. 创建配置文件(从示例复制) +sudo cp /opt/linkmaster-node/config.yaml.example /opt/linkmaster-node/config.yaml +# 编辑配置文件,设置后端地址 +sudo nano /opt/linkmaster-node/config.yaml + +# 6. 创建 systemd 服务(参考下面的服务配置) +``` + +### 方式二:克隆源码并编译 ```bash # 克隆仓库 diff --git a/README.md b/README.md index 07273bf..91bebe4 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,7 @@ GITHUB_BRANCH=develop curl -fsSL https://gitee.nas.cpolar.cn/yoyo/linkmaster-nod - 自动安装系统依赖(curl, wget, git, ping, traceroute 等) - 自动安装 Go 环境(优先使用系统包管理器,失败则从官网下载) - 优先从 Releases 下载预编译二进制文件,失败则从源码编译 +- **发布包包含所有必要文件**:二进制文件、安装脚本、运行脚本等,无需从 Git 拉取 - 自动创建 systemd 服务并配置自启动 - 自动配置防火墙规则(开放 2200 端口) - 自动登记节点到后端服务器 @@ -411,6 +412,7 @@ BACKEND_URL=http://192.168.1.100:8080 ./run.sh start **功能特性:** - ✅ **自动从 `version.json` 读取版本号和标签**(无需手动指定) - ✅ **Token 已硬编码**(无需手动指定) +- ✅ **自动打包所有必要文件**:二进制文件、安装脚本、运行脚本、配置文件等 - ✅ 自动打包二进制文件(tar.gz 或 zip) - ✅ 自动创建发布说明 - ✅ 支持指定平台上传 diff --git a/all-upload-release.sh b/all-upload-release.sh index c7b62d9..66975de 100755 --- a/all-upload-release.sh +++ b/all-upload-release.sh @@ -279,16 +279,52 @@ pack_files() { local pack_name="${PROJECT_NAME}-${os}-${arch}-${VERSION}" local pack_file + # 创建临时打包目录 + local pack_dir="${TEMP_DIR}/${pack_name}" + mkdir -p "$pack_dir" + + # 复制二进制文件并重命名为 agent + if [ "$os" = "windows" ]; then + cp "$binary" "$pack_dir/agent.exe" + else + cp "$binary" "$pack_dir/agent" + chmod +x "$pack_dir/agent" + fi + + # 复制必要的脚本文件 + local scripts=("install.sh" "run.sh" "start-systemd.sh" "uninstall.sh") + for script in "${scripts[@]}"; do + if [ -f "$script" ]; then + cp "$script" "$pack_dir/" + chmod +x "$pack_dir/$script" + echo -e "${BLUE} [包含]${NC} $script" + else + echo -e "${YELLOW} [警告]${NC} $script 不存在,跳过" + fi + done + + # 复制示例配置文件 + if [ -f "config.yaml.example" ]; then + cp "config.yaml.example" "$pack_dir/config.yaml.example" + echo -e "${BLUE} [包含]${NC} config.yaml.example" + else + echo -e "${YELLOW} [警告]${NC} config.yaml.example 不存在,跳过" + fi + + # 打包 if [ "$os" = "windows" ]; then pack_file="${TEMP_DIR}/${pack_name}.zip" echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.zip" - (cd "$BUILD_DIR" && zip -q "${pack_file}" "$(basename $binary)") + (cd "$TEMP_DIR" && zip -q -r "${pack_file}" "$(basename $pack_dir)") else pack_file="${TEMP_DIR}/${pack_name}.tar.gz" echo -e "${BLUE}[打包]${NC} ${platform} -> ${pack_name}.tar.gz" - tar -czf "$pack_file" -C "$BUILD_DIR" "$(basename $binary)" + tar -czf "$pack_file" -C "$TEMP_DIR" "$(basename $pack_dir)" fi + # 清理临时目录 + rm -rf "$pack_dir" + echo "$pack_file" return 0 } diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..39d19b1 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,18 @@ +server: + port: 2200 +backend: + url: http://your-backend-server:8080 +heartbeat: + interval: 60 +log: + file: node.log + level: info +debug: false +node: + id: 0 + ip: "" + country: "" + province: "" + city: "" + isp: "" + diff --git a/install.sh b/install.sh index f15dfcf..7f9eeaf 100755 --- a/install.sh +++ b/install.sh @@ -833,21 +833,20 @@ download_binary_from_releases() { fi # 解压文件 - echo -e "${BLUE}解压二进制文件...${NC}" + echo -e "${BLUE}解压发布包...${NC}" cd "$temp_dir" + local extracted_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) + # 查找解压后的目录(可能是直接解压到当前目录,也可能是在子目录中) + extracted_dir=$(find . -maxdepth 1 -type d ! -name "." ! -name ".." | head -1) + if [ -z "$extracted_dir" ]; then + extracted_dir="." fi else # Windows zip 文件(虽然脚本主要在 Linux 上运行,但保留兼容性) @@ -856,26 +855,82 @@ download_binary_from_releases() { 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) + extracted_dir=$(find . -maxdepth 1 -type d ! -name "." ! -name ".." | head -1) + if [ -z "$extracted_dir" ]; then + extracted_dir="." + fi + fi + + cd "$extracted_dir" || { + echo -e "${YELLOW}⚠ 无法进入解压目录,将使用源码编译${NC}" + rm -rf "$temp_dir" + return 1 + } + + # 显示解压后的目录内容(用于调试) + echo -e "${BLUE}解压目录内容:${NC}" + ls -la . 2>/dev/null || true + echo "" + + # 查找二进制文件(先检查当前目录,再递归查找) + local binary_file="" + if [ "$OS_TYPE" = "windows" ]; then + if [ -f "./agent.exe" ]; then + binary_file="./agent.exe" + elif [ -f "agent.exe" ]; then + binary_file="agent.exe" + else + # 递归查找所有 .exe 文件 + binary_file=$(find . -type f -name "*.exe" ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | grep -i agent | head -1) + fi + else + # Linux/macOS: 先检查常见位置 + if [ -f "./agent" ]; then + binary_file="./agent" + elif [ -f "agent" ]; then + binary_file="agent" + else + # 递归查找所有文件,排除压缩包和目录 + # 查找名为 agent 的文件(不是目录) + binary_file=$(find . -type f \( -name "agent" -o -name "agent-*" \) ! -name "*.tar.gz" ! -name "*.zip" 2>/dev/null | head -1) + + # 如果还是找不到,尝试查找所有可执行文件 + if [ -z "$binary_file" ]; then + binary_file=$(find . -type f -perm +111 ! -name "*.tar.gz" ! -name "*.zip" ! -name "*.sh" 2>/dev/null | head -1) + fi + fi 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 + echo -e "${YELLOW} 当前目录: $(pwd)${NC}" + echo -e "${YELLOW} 查找的文件: agent 或 agent-*${NC}" + echo -e "${YELLOW} 所有文件列表:${NC}" + find . -type f 2>/dev/null | head -20 || true rm -rf "$temp_dir" return 1 fi + # 确保使用绝对路径 + local binary_path="" + if [[ "$binary_file" == /* ]]; then + binary_path="$binary_file" + else + # 转换为绝对路径 + binary_path="$(cd "$(dirname "$binary_file")" && pwd)/$(basename "$binary_file")" + fi + + echo -e "${GREEN}✓ 找到二进制文件: ${binary_path}${NC}" + # 验证二进制文件是否可执行 - if [ ! -x "$binary_file" ]; then - chmod +x "$binary_file" 2>/dev/null || true + if [ ! -x "$binary_path" ]; then + chmod +x "$binary_path" 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 "") + local file_type=$(file "$binary_path" 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}" @@ -885,93 +940,98 @@ download_binary_from_releases() { fi fi - # 保存下载的二进制文件到临时位置 - local downloaded_binary="${temp_dir}/downloaded_agent" - sudo cp "$binary_file" "$downloaded_binary" - sudo chmod +x "$downloaded_binary" + # 保存当前目录(extracted_dir) + local extracted_path="$(pwd)" - # 验证复制后的文件 - if [ ! -f "$downloaded_binary" ] || [ ! -x "$downloaded_binary" ]; then - echo -e "${YELLOW}⚠ 二进制文件验证失败,将使用源码编译${NC}" - rm -rf "$temp_dir" - return 1 + # 检查是否是新格式的发布包(包含脚本文件) + local has_scripts=false + if [ -f "$extracted_path/install.sh" ] || [ -f "$extracted_path/run.sh" ] || [ -f "$extracted_path/start-systemd.sh" ]; then + has_scripts=true + echo -e "${GREEN}✓ 检测到新格式发布包(包含脚本文件)${NC}" 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 + sudo mkdir -p "$SOURCE_DIR" - 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}" + if [ "$has_scripts" = true ]; then + # 新格式:从压缩包提取所有文件 + echo -e "${BLUE}从发布包提取所有文件...${NC}" - # 切换到源码目录(如果存在) - if [ -d "$SOURCE_DIR" ] && [ -d "$SOURCE_DIR/.git" ]; then - cd "$SOURCE_DIR" || { - echo -e "${YELLOW}⚠ 无法切换到源码目录,跳过验证${NC}" - cd /tmp || true - } + # 复制二进制文件 + sudo cp "$binary_path" "$SOURCE_DIR/agent" + sudo chmod +x "$SOURCE_DIR/agent" + echo -e "${GREEN}✓ 已提取二进制文件${NC}" + + # 复制脚本文件 + local scripts=("install.sh" "run.sh" "start-systemd.sh" "uninstall.sh") + for script in "${scripts[@]}"; do + if [ -f "$extracted_path/$script" ]; then + sudo cp "$extracted_path/$script" "$SOURCE_DIR/" + sudo chmod +x "$SOURCE_DIR/$script" + echo -e "${GREEN}✓ 已提取 $script${NC}" + fi + done + + # 复制示例配置文件 + if [ -f "$extracted_path/config.yaml.example" ]; then + sudo cp "$extracted_path/config.yaml.example" "$SOURCE_DIR/config.yaml.example" + echo -e "${GREEN}✓ 已提取 config.yaml.example${NC}" + fi + + echo -e "${GREEN}✓ 所有文件已从发布包提取,无需克隆 Git 仓库${NC}" + else + # 旧格式:只有二进制文件,需要克隆 Git 仓库获取脚本 + echo -e "${BLUE}检测到旧格式发布包(仅包含二进制文件)${NC}" + echo -e "${BLUE}克隆仓库以获取启动脚本...${NC}" + + 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}" - # 获取当前分支的最新 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="" - local current_commit_short=$(echo "$current_commit" | cut -c1-7) + if [ -d "$SOURCE_DIR/.git" ]; then + cd "$SOURCE_DIR" || { + echo -e "${YELLOW}⚠ 无法切换到源码目录,跳过验证${NC}" + cd /tmp || true + } - if [ "${#release_commit}" -eq 40 ]; then - release_commit_short=$(echo "$release_commit" | cut -c1-7) - else - release_commit_short="$release_commit" - fi + local current_commit=$(git rev-parse HEAD 2>/dev/null || echo "") - echo -e "${BLUE} Release commit: ${release_commit_short}${NC}" - echo -e "${BLUE} 当前代码 commit: ${current_commit_short}${NC}" - - # 对比 commit hash(只有当 release_commit 是完整的 commit hash 时才对比) - if [ "${#release_commit}" -eq 40 ] && [ "${#current_commit}" -eq 40 ]; then + if [ -n "$current_commit" ] && [ "${#release_commit}" -eq 40 ] && [ "${#current_commit}" -eq 40 ]; then + 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}" + if [ "$release_commit" != "$current_commit" ]; then echo -e "${YELLOW}⚠ Commit hash 不匹配,二进制文件可能不是最新代码编译的${NC}" echo -e "${YELLOW} Release 基于较旧的代码,将使用源码编译最新版本${NC}" - # 保留已克隆的仓库目录,供 build_from_source 复用 cd /tmp || true rm -rf "$temp_dir" return 1 else - echo -e "${GREEN}✓ Commit hash 匹配,二进制文件是最新代码编译的${NC}" + echo -e "${GREEN}✓ Commit hash 匹配${NC}" fi - else - echo -e "${YELLOW}⚠ 无法获取有效的 commit hash 进行对比,跳过验证${NC}" fi - else - echo -e "${YELLOW}⚠ 无法获取当前代码的 commit hash,跳过验证${NC}" + + cd /tmp || true fi - - # 返回临时目录 - cd /tmp || true - else - echo -e "${YELLOW}⚠ 源码目录不存在,跳过验证${NC}" fi - else - echo -e "${YELLOW}⚠ 无法获取 release 的 commit hash,跳过验证${NC}" + + # 用下载的二进制文件覆盖克隆目录中的文件 + sudo cp "$binary_path" "$SOURCE_DIR/agent" + sudo chmod +x "$SOURCE_DIR/agent" 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" @@ -982,9 +1042,9 @@ download_binary_from_releases() { # 显示文件信息 local binary_size=$(du -h "$SOURCE_DIR/agent" | cut -f1) - echo -e "${GREEN}✓ 二进制文件下载完成 (文件大小: ${binary_size})${NC}" + echo -e "${GREEN}✓ 安装文件准备完成 (文件大小: ${binary_size})${NC}" echo -e "${BLUE}版本: ${latest_tag}${NC}" - echo -e "${BLUE}二进制文件: ${SOURCE_DIR}/agent${NC}" + echo -e "${BLUE}安装目录: ${SOURCE_DIR}${NC}" return 0 }