#!/usr/bin/env bash # ============================================================================= # build_ota_package.sh - OTA 升级包构建脚本 # # 用法: # ./build_ota_package.sh -v 1.2.0 -n "修复内存泄漏问题" # ./build_ota_package.sh -v 1.2.0 -n "新增模型支持" --upload # # 功能: # 1. 编译 Go 边缘代理二进制文件 (linux/arm64) # 2. 打包为 tar.gz 格式 # 3. 计算 SHA256 校验和 # 4. 可选: 自动发布到云端 OTA 服务 # ============================================================================= set -euo pipefail # ── 默认配置 ───────────────────────────────────────────────────────────────── VERSION="" RELEASE_NOTES="" UPLOAD=false OTA_SERVER_URL="http://127.0.0.1:9000" OTA_ADMIN_TOKEN="" # ── 项目路径 ───────────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" EDGE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" BUILD_DIR="${EDGE_DIR}/build/ota" PACKAGE_DIR="${BUILD_DIR}/package" # ── 参数解析 ───────────────────────────────────────────────────────────────── usage() { echo "用法: $0 -v <版本号> [-n \"发布说明\"] [--upload] [--url ]" echo "" echo "选项:" echo " -v 版本号 (必填, 格式: X.Y.Z 或 X.Y.Z-beta)" echo " -n 发布说明 (可选)" echo " --upload 构建后自动发布到云端 OTA 服务" echo " --url OTA 服务器 URL (默认: http://127.0.0.1:9000)" echo " --token 管理 API Token (可选)" echo "" echo "示例:" echo " $0 -v 1.2.0 -n \"修复内存泄漏\"" echo " $0 -v 1.2.0 -n \"新增安全帽检测\" --upload --url http://101.36.73.102:9000" exit 1 } while [[ $# -gt 0 ]]; do case $1 in -v) VERSION="$2" shift 2 ;; -n) RELEASE_NOTES="$2" shift 2 ;; --upload) UPLOAD=true shift ;; --url) OTA_SERVER_URL="$2" shift 2 ;; --token) OTA_ADMIN_TOKEN="$2" shift 2 ;; -h|--help) usage ;; *) echo "未知参数: $1" usage ;; esac done # ── 参数验证 ───────────────────────────────────────────────────────────────── if [[ -z "$VERSION" ]]; then echo "错误: 版本号是必填参数 (-v)" usage fi if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$'; then echo "错误: 版本号格式不正确,请使用 X.Y.Z 或 X.Y.Z-beta 格式" exit 1 fi echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 构建 OTA 升级包" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 版本: $VERSION" echo " 发布说明: ${RELEASE_NOTES:-无}" echo " 边缘项目: ${EDGE_DIR}" echo " 构建目录: ${BUILD_DIR}" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # ── 清理旧构建 ────────────────────────────────────────────────────────────── rm -rf "${BUILD_DIR}" mkdir -p "${PACKAGE_DIR}" # ── 编译 Go 二进制 ────────────────────────────────────────────────────────── echo "" echo ">>> 准备二进制文件..." if command -v go &>/dev/null && [[ -f "${EDGE_DIR}/cmd/edge-agent/main.go" ]]; then echo " 检测到 Go 环境,编译二进制 (linux/arm64)..." cd "${EDGE_DIR}" CGO_ENABLED=0 GOOS=linux GOARCH=arm64 \ go build -ldflags "-s -w -X main.Version=${VERSION}" \ -o "${PACKAGE_DIR}/edge-agent" \ ./cmd/edge-agent BINARY_SIZE=$(stat -c%s "${PACKAGE_DIR}/edge-agent") echo " 编译成功: $(numfmt --to=iec "${BINARY_SIZE}")" elif [[ -f "${EDGE_DIR}/edge-agent" ]]; then echo " 使用预编译二进制: ${EDGE_DIR}/edge-agent" cp "${EDGE_DIR}/edge-agent" "${PACKAGE_DIR}/edge-agent" BINARY_SIZE=$(stat -c%s "${PACKAGE_DIR}/edge-agent") elif [[ -f "${EDGE_DIR}/build/edge-agent" ]]; then echo " 使用预编译二进制: ${EDGE_DIR}/build/edge-agent" cp "${EDGE_DIR}/build/edge-agent" "${PACKAGE_DIR}/edge-agent" BINARY_SIZE=$(stat -c%s "${PACKAGE_DIR}/edge-agent") else echo "错误: 未找到 Go 编译器或预编译二进制文件" echo " 解决方案:" echo " 1. 安装 Go: sudo snap install go --classic" echo " 2. 或预先编译二进制: cd ${EDGE_DIR} && CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o build/edge-agent ./cmd/edge-agent" exit 1 fi # ── 设置文件权限 ───────────────────────────────────────────────────────────── chmod +x "${PACKAGE_DIR}/edge-agent" # ── 打包 ───────────────────────────────────────────────────────────────────── PACKAGE_FILE="${BUILD_DIR}/edge-agent-${VERSION}.tar.gz" echo "" echo ">>> 创建升级包..." cd "${PACKAGE_DIR}" tar czf "${PACKAGE_FILE}" edge-agent PACKAGE_SIZE=$(stat -c%s "${PACKAGE_FILE}") echo " 打包完成: ${PACKAGE_FILE} ($(numfmt --to=iec "${PACKAGE_SIZE}"))" # ── 计算 SHA256 ────────────────────────────────────────────────────────────── SHA256=$(sha256sum "${PACKAGE_FILE}" | awk '{print $1}') echo " SHA256: ${SHA256}" # ── 生成版本信息文件 ───────────────────────────────────────────────────────── cat > "${BUILD_DIR}/version-info.json" << EOF { "version": "${VERSION}", "sha256": "${SHA256}", "file_size": ${PACKAGE_SIZE}, "release_notes": "${RELEASE_NOTES}", "build_time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "arch": "arm64", "binary_size": ${BINARY_SIZE} } EOF echo " 版本信息: ${BUILD_DIR}/version-info.json" # ── 自动上传 (可选) ───────────────────────────────────────────────────────── if [[ "$UPLOAD" == true ]]; then echo "" echo ">>> 发布到云端 OTA 服务: ${OTA_SERVER_URL}..." CURL_ARGS=( "-s" "-X" "POST" "${OTA_SERVER_URL}/api/v1/edge/update/admin/publish" "-F" "version=${VERSION}" "-F" "release_notes=${RELEASE_NOTES}" "-F" "maintenance_only=true" "-F" "target_arch=arm64" "-F" "package=@${PACKAGE_FILE}" ) if [[ -n "$OTA_ADMIN_TOKEN" ]]; then CURL_ARGS+=("-H" "Authorization: Bearer ${OTA_ADMIN_TOKEN}") fi RESPONSE=$("${CURL_ARGS[@]}") echo " 服务器响应: ${RESPONSE}" if echo "$RESPONSE" | grep -q '"ok": true'; then echo " 发布成功!" else echo " 警告: 服务器返回非成功响应" fi fi # ── 完成 ───────────────────────────────────────────────────────────────────── echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 构建完成!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " 升级包路径: ${PACKAGE_FILE}" echo " 版本信息: ${BUILD_DIR}/version-info.json" echo "" echo " 手动发布到 OTA 服务:" echo " curl -X POST ${OTA_SERVER_URL}/api/v1/edge/update/admin/publish \\" echo " -F \"version=${VERSION}\" \\" echo " -F \"release_notes=${RELEASE_NOTES}\" \\" echo " -F \"package=@${PACKAGE_FILE}\"" echo "" echo " 边缘侧升级命令 (在设备上执行):" echo " curl -o /tmp/update.tar.gz ${OTA_SERVER_URL}/api/v1/edge/update/packages/${VERSION}" echo " sudo systemctl stop edge-agent" echo " sudo tar xzf /tmp/update.tar.gz -C /opt/tianyan-edge/bin/" echo " sudo systemctl start edge-agent" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"