#!/usr/bin/env bash set -Eeuo pipefail # 日志源目录:由日志生产服务写入 # 默认值:/home/basil/bot/data/git-summary LOG_SOURCE_DIR="${LOG_SOURCE_DIR:-/home/basil/bot/data/git-summary}" # 个人主页仓库目录 # 默认值:/home/basil/source/personal-homepage SITE_DIR="${SITE_DIR:-/home/basil/source/personal-homepage}" # 日志目标目录:复制到 homepage 仓库内 # 默认值:$SITE_DIR/src/content/logs LOG_TARGET_DIR="${LOG_TARGET_DIR:-${SITE_DIR}/src/content/logs}" # Docker Compose 配置与服务名 # 默认 compose 文件:$SITE_DIR/docker-compose.yml # 默认服务名:homepage DOCKER_COMPOSE_FILE="${DOCKER_COMPOSE_FILE:-${SITE_DIR}/docker-compose.yml}" DOCKER_SERVICE_NAME="${DOCKER_SERVICE_NAME:-homepage}" # 是否在同步时删除目标目录中源目录已不存在的文件 # 默认值:false RSYNC_DELETE="${RSYNC_DELETE:-false}" # 是否先在宿主机执行一次 npm run rebuild # 默认值:true RUN_LOCAL_REBUILD="${RUN_LOCAL_REBUILD:-true}" # 锁文件路径,避免定时任务重入 # 默认值:/tmp/personal-homepage-deploy.lock LOCK_FILE="${LOCK_FILE:-/tmp/personal-homepage-deploy.lock}" timestamp() { date '+%Y-%m-%d %H:%M:%S' } log() { printf '[%s] %s\n' "$(timestamp)" "$*" } fail() { log "ERROR: $*" exit 1 } require_command() { command -v "$1" >/dev/null 2>&1 || fail "缺少命令: $1" } is_true() { case "${1,,}" in 1|true|yes|on) return 0 ;; *) return 1 ;; esac } sync_logs() { mkdir -p "$LOG_TARGET_DIR" if command -v rsync >/dev/null 2>&1; then local -a rsync_args=(-av) if is_true "$RSYNC_DELETE"; then rsync_args+=(--delete) fi log "同步日志: $LOG_SOURCE_DIR -> $LOG_TARGET_DIR" rsync "${rsync_args[@]}" "${LOG_SOURCE_DIR}/" "${LOG_TARGET_DIR}/" return 0 fi log "未找到 rsync,改用 cp -a 复制日志" if is_true "$RSYNC_DELETE"; then find "$LOG_TARGET_DIR" -mindepth 1 -maxdepth 1 -exec rm -rf {} + fi cp -a "${LOG_SOURCE_DIR}/." "$LOG_TARGET_DIR/" } run_local_rebuild() { log "开始宿主机构建校验: npm run rebuild" ( cd "$SITE_DIR" npm run rebuild ) } refresh_docker_service() { log "刷新 Docker 服务: $DOCKER_SERVICE_NAME" ( cd "$SITE_DIR" docker compose -f "$DOCKER_COMPOSE_FILE" up -d --build "$DOCKER_SERVICE_NAME" ) } main_impl() { require_command docker if is_true "$RUN_LOCAL_REBUILD"; then require_command npm fi docker compose version >/dev/null 2>&1 || fail "当前环境不可用 docker compose" [[ -d "$LOG_SOURCE_DIR" ]] || fail "日志源目录不存在: $LOG_SOURCE_DIR" [[ -d "$SITE_DIR" ]] || fail "站点目录不存在: $SITE_DIR" [[ -f "$DOCKER_COMPOSE_FILE" ]] || fail "docker compose 文件不存在: $DOCKER_COMPOSE_FILE" log "部署开始" log "LOG_SOURCE_DIR=$LOG_SOURCE_DIR" log "LOG_TARGET_DIR=$LOG_TARGET_DIR" log "SITE_DIR=$SITE_DIR" log "DOCKER_COMPOSE_FILE=$DOCKER_COMPOSE_FILE" log "DOCKER_SERVICE_NAME=$DOCKER_SERVICE_NAME" log "RSYNC_DELETE=$RSYNC_DELETE" log "RUN_LOCAL_REBUILD=$RUN_LOCAL_REBUILD" log "LOCK_FILE=$LOCK_FILE" sync_logs if is_true "$RUN_LOCAL_REBUILD"; then run_local_rebuild else log "跳过宿主机 npm run rebuild(RUN_LOCAL_REBUILD=false)" fi refresh_docker_service log "部署完成" } main() { if ! command -v flock >/dev/null 2>&1; then log "未找到 flock,跳过锁保护" main_impl "$@" return 0 fi mkdir -p "$(dirname "$LOCK_FILE")" exec 9>"$LOCK_FILE" if ! flock -n 9; then fail "已有部署任务在执行中,锁文件: $LOCK_FILE" fi log "已获取部署锁: $LOCK_FILE" main_impl "$@" } main "$@"