180 lines
4.5 KiB
Bash
Executable File
180 lines
4.5 KiB
Bash
Executable File
#!/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}"
|
||
|
||
# 静态资源同步清单
|
||
# 默认值:$SITE_DIR/src/content/static-assets/index.json
|
||
STATIC_ASSET_MANIFEST="${STATIC_ASSET_MANIFEST:-src/content/static-assets/index.json}"
|
||
|
||
# 个人主页仓库目录
|
||
# 默认值:/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}"
|
||
|
||
# 是否在 log 同步前先同步静态资源
|
||
# 默认值:true
|
||
RUN_STATIC_ASSET_SYNC="${RUN_STATIC_ASSET_SYNC:-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/"
|
||
}
|
||
|
||
sync_static_assets() {
|
||
log "同步静态资源清单: $STATIC_ASSET_MANIFEST"
|
||
(
|
||
cd "$SITE_DIR"
|
||
STATIC_ASSET_MANIFEST="$STATIC_ASSET_MANIFEST" \
|
||
node --env-file-if-exists=.env --experimental-strip-types ./scripts/sync-static-assets.ts
|
||
)
|
||
}
|
||
|
||
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_STATIC_ASSET_SYNC"; then
|
||
require_command node
|
||
fi
|
||
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 "STATIC_ASSET_MANIFEST=$STATIC_ASSET_MANIFEST"
|
||
log "DOCKER_COMPOSE_FILE=$DOCKER_COMPOSE_FILE"
|
||
log "DOCKER_SERVICE_NAME=$DOCKER_SERVICE_NAME"
|
||
log "RSYNC_DELETE=$RSYNC_DELETE"
|
||
log "RUN_STATIC_ASSET_SYNC=$RUN_STATIC_ASSET_SYNC"
|
||
log "RUN_LOCAL_REBUILD=$RUN_LOCAL_REBUILD"
|
||
log "LOCK_FILE=$LOCK_FILE"
|
||
|
||
if is_true "$RUN_STATIC_ASSET_SYNC"; then
|
||
sync_static_assets
|
||
else
|
||
log "跳过静态资源同步(RUN_STATIC_ASSET_SYNC=false)"
|
||
fi
|
||
|
||
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 "$@"
|