#!/usr/bin/env bash set -Eeuo pipefail VERSION="" TARGET="" OUTPUT_DIR="target/releases" SKIP_BUILD="false" usage() { cat <<'EOF' LightOps 发布包打包脚本 用法: bash scripts/build-release.sh [选项] 选项: --version 发布版本,默认读取 Cargo.toml workspace 版本并追加 git 短提交 --target Rust target triple,例如 x86_64-unknown-linux-gnu --output-dir 输出目录,默认 target/releases --skip-build 跳过构建,只打包现有产物 -h, --help 显示帮助 EOF } while [[ $# -gt 0 ]]; do case "$1" in --version) VERSION="${2:?缺少 --version 参数值}" shift 2 ;; --target) TARGET="${2:?缺少 --target 参数值}" shift 2 ;; --output-dir) OUTPUT_DIR="${2:?缺少 --output-dir 参数值}" shift 2 ;; --skip-build) SKIP_BUILD="true" shift ;; -h|--help) usage exit 0 ;; *) echo "未知参数:$1" >&2 usage >&2 exit 2 ;; esac done log() { printf '\033[1;32m[LightOps]\033[0m %s\n' "$*" } fail() { printf '\033[1;31m[LightOps]\033[0m %s\n' "$*" >&2 exit 1 } require_cmd() { command -v "$1" >/dev/null 2>&1 || fail "缺少命令:$1" } repo_root() { git rev-parse --show-toplevel 2>/dev/null || pwd } detect_version() { local base sha base="$(sed -nE '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p}' Cargo.toml | head -n 1)" if [[ -z "$base" ]]; then base="0.1.0" fi sha="$(git rev-parse --short HEAD 2>/dev/null || true)" if [[ -n "$sha" ]]; then echo "${base}-${sha}" else echo "$base" fi } detect_platform() { local os arch os="$(uname -s | tr '[:upper:]' '[:lower:]')" arch="$(uname -m)" case "$arch" in x86_64|amd64) arch="x86_64" ;; aarch64|arm64) arch="aarch64" ;; esac echo "${arch}-${os}" } ROOT="$(repo_root)" cd "$ROOT" require_cmd tar if [[ "$SKIP_BUILD" != "true" ]]; then require_cmd npm require_cmd cargo fi if [[ -z "$VERSION" ]]; then VERSION="$(detect_version)" fi PLATFORM="${TARGET:-$(detect_platform)}" PACKAGE_NAME="lightops-${VERSION}-${PLATFORM}" STAGE_DIR="target/release-package/${PACKAGE_NAME}" ARCHIVE_PATH="${OUTPUT_DIR}/${PACKAGE_NAME}.tar.gz" HOST_TARGET="$(rustc -vV 2>/dev/null | sed -n 's/^host: //p')" NATIVE_TARGET="false" if [[ -n "$TARGET" && -n "$HOST_TARGET" && "$TARGET" == "$HOST_TARGET" ]]; then NATIVE_TARGET="true" fi if [[ "$SKIP_BUILD" != "true" ]]; then log "构建前端" cd "$ROOT/web" if [[ -f package-lock.json ]]; then npm ci else npm install fi npm run build log "构建 Rust 二进制" cd "$ROOT" if [[ -n "$TARGET" && "$NATIVE_TARGET" != "true" ]]; then cargo build --release --target "$TARGET" -p lightops-server -p lightops-agent BIN_DIR="target/${TARGET}/release" else cargo build --release -p lightops-server -p lightops-agent BIN_DIR="target/release" fi else cd "$ROOT" if [[ -n "$TARGET" && "$NATIVE_TARGET" != "true" ]]; then BIN_DIR="target/${TARGET}/release" else BIN_DIR="target/release" fi fi SERVER_BIN="${BIN_DIR}/lightops-server" AGENT_BIN="${BIN_DIR}/lightops-agent" if [[ "$(uname -s)" == MINGW* || "$(uname -s)" == MSYS* || "$(uname -s)" == CYGWIN* ]]; then [[ -f "${SERVER_BIN}.exe" ]] && SERVER_BIN="${SERVER_BIN}.exe" [[ -f "${AGENT_BIN}.exe" ]] && AGENT_BIN="${AGENT_BIN}.exe" fi [[ -f "$SERVER_BIN" ]] || fail "缺少 lightops-server 构建产物:$SERVER_BIN" [[ -f "$AGENT_BIN" ]] || fail "缺少 lightops-agent 构建产物:$AGENT_BIN" [[ -d "$ROOT/web/dist" ]] || fail "缺少前端构建产物:web/dist" log "整理发布目录:$STAGE_DIR" rm -rf "$STAGE_DIR" mkdir -p "$STAGE_DIR/bin" "$STAGE_DIR/web" "$STAGE_DIR/scripts" "$STAGE_DIR/config" "$OUTPUT_DIR" install -m 0755 "$SERVER_BIN" "$STAGE_DIR/bin/lightops-server" install -m 0755 "$AGENT_BIN" "$STAGE_DIR/bin/lightops-agent" cp -a "$ROOT/web/dist" "$STAGE_DIR/web/dist" cp -a "$ROOT/store" "$STAGE_DIR/store" install -m 0755 "$ROOT/scripts/install-server-release.sh" "$STAGE_DIR/scripts/install-server-release.sh" install -m 0755 "$ROOT/scripts/install-agent.sh" "$STAGE_DIR/scripts/install-agent.sh" install -m 0755 "$ROOT/scripts/upgrade-agent.sh" "$STAGE_DIR/scripts/upgrade-agent.sh" install -m 0755 "$ROOT/scripts/uninstall-agent.sh" "$STAGE_DIR/scripts/uninstall-agent.sh" install -m 0755 "$ROOT/scripts/update-from-git.sh" "$STAGE_DIR/scripts/update-from-git.sh" cp "$ROOT/config/server.toml.example" "$STAGE_DIR/config/server.toml.example" cat >"$STAGE_DIR/RELEASE.txt" </dev/null || echo unknown) EOF log "生成压缩包:$ARCHIVE_PATH" tar -C "$(dirname "$STAGE_DIR")" -czf "$ARCHIVE_PATH" "$(basename "$STAGE_DIR")" if command -v sha256sum >/dev/null 2>&1; then sha256sum "$ARCHIVE_PATH" >"${ARCHIVE_PATH}.sha256" elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$ARCHIVE_PATH" >"${ARCHIVE_PATH}.sha256" fi cat < EOF