253 lines
6.7 KiB
Bash
Executable File
253 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# ============================================================
|
||
# 统一部署脚本:将本地产物同步到正式服务器(rsync over ssh)
|
||
#
|
||
# 覆盖两类目标(统一 ssh 别名 luxsin-api2,按顶层目录自动路由远程路径):
|
||
# dashboard/ -> /data/project/dashboard/ (frontend / backend / compose)
|
||
# www/ -> /data/project/dashboard/www/ (默认仅 .output/;nginx.conf / docker-compose.yml 需显式指定)
|
||
#
|
||
# 用法:./deploy/upload.sh [选项] [目标|路径...]
|
||
# ============================================================
|
||
set -euo pipefail
|
||
|
||
# ===== 连接(统一使用 ssh 别名) =====
|
||
SERVER="luxsin-api2"
|
||
REMOTE_ROOT="/data/project/dashboard"
|
||
# www 静态产物位于 ${REMOTE_ROOT}/www
|
||
WWW_REMOTE_ROOT="${REMOTE_ROOT}/www"
|
||
|
||
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||
|
||
FRONTEND_PATHS=(
|
||
./dashboard/frontend/dist/
|
||
)
|
||
|
||
BACKEND_PATHS=(
|
||
./dashboard/backend/dist/
|
||
./dashboard/backend/package.json
|
||
./dashboard/backend/pnpm-lock.yaml
|
||
./dashboard/backend/pnpm-workspace.yaml
|
||
)
|
||
|
||
COMPOSE_PATHS=(
|
||
./dashboard/docker-compose.yml
|
||
)
|
||
|
||
WWW_PATHS=(
|
||
./www/.output/
|
||
)
|
||
|
||
DEFAULT_PATHS=(
|
||
"${FRONTEND_PATHS[@]}"
|
||
"${BACKEND_PATHS[@]}"
|
||
"${COMPOSE_PATHS[@]}"
|
||
)
|
||
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
|
||
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||
|
||
show_help() {
|
||
cat <<EOF
|
||
用法: $(basename "$0") [选项] [目标|路径...]
|
||
|
||
将本地代码同步到远程服务器(rsync)。
|
||
|
||
远程(统一 ssh 别名 ${SERVER},按顶层目录自动路由):
|
||
dashboard/ -> ${SERVER}:${REMOTE_ROOT}/
|
||
www/ -> ${SERVER}:${WWW_REMOTE_ROOT}/
|
||
|
||
选项:
|
||
-n 虚拟执行,不实际传输或删除远程文件
|
||
-h, --help 显示此帮助信息
|
||
|
||
目标:
|
||
frontend, f 同步 dashboard 前端 dist/(默认跳过 uploads/)
|
||
backend, b 同步 dashboard 后端 dist/、package.json、pnpm-lock.yaml
|
||
compose, c 同步 dashboard docker-compose.yml
|
||
www, w 同步 www 构建产物 .output/(nginx.conf / docker-compose.yml 需显式指定路径)
|
||
all, a 同步 frontend + backend + compose(不含 www)
|
||
(路径) 同步指定文件或目录,可传多个
|
||
|
||
默认不同步 nginx.conf 与 Dockerfile(dashboard),需要时请显式指定,例如:
|
||
./dashboard/frontend/nginx.conf
|
||
./dashboard/backend/Dockerfile
|
||
|
||
www 同步前需先本地构建: cd www && pnpm generate
|
||
|
||
示例:
|
||
$(basename "$0") 同步 dashboard 默认内容
|
||
$(basename "$0") frontend 仅同步 dashboard 前端
|
||
$(basename "$0") www 仅同步 www .output/
|
||
$(basename "$0") www ./www/nginx.conf ./www/docker-compose.yml 同步 www 配置
|
||
$(basename "$0") backend www 同步后端与 www
|
||
$(basename "$0") all www 同步全部(dashboard + www)
|
||
$(basename "$0") -n www 虚拟执行,预览 www 同步
|
||
EOF
|
||
}
|
||
|
||
resolve_preset_paths() {
|
||
local preset="$1"
|
||
case "$preset" in
|
||
frontend|f) printf '%s\n' "${FRONTEND_PATHS[@]}" ;;
|
||
backend|b) printf '%s\n' "${BACKEND_PATHS[@]}" ;;
|
||
compose|c) printf '%s\n' "${COMPOSE_PATHS[@]}" ;;
|
||
www|w) printf '%s\n' "${WWW_PATHS[@]}" ;;
|
||
all|a) printf '%s\n' "${DEFAULT_PATHS[@]}" ;;
|
||
*)
|
||
echo "$preset"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# www 静态产物同步前校验本地已构建(nuxt generate)
|
||
ensure_www_built() {
|
||
local idx="${PROJECT_ROOT}/www/.output/public/index.html"
|
||
if [[ ! -f "$idx" ]]; then
|
||
echo "❌ 本地缺少 ${idx},请先执行:"
|
||
echo " cd ${PROJECT_ROOT}/www && pnpm generate"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
rsync_item() {
|
||
local item="$1"
|
||
local rel local_full
|
||
|
||
if [[ "$item" == /* ]]; then
|
||
if [[ "$item" == "$PROJECT_ROOT"/* ]]; then
|
||
rel="${item#"$PROJECT_ROOT"/}"
|
||
local_full="$item"
|
||
else
|
||
warn "路径不在项目内,跳过: $item"
|
||
return 1
|
||
fi
|
||
else
|
||
rel="${item#./}"
|
||
rel="${rel%/}"
|
||
local_full="${PROJECT_ROOT}/${rel}"
|
||
fi
|
||
|
||
# 依据顶层目录映射远程路径(dashboard/ 内容在远程根,www/ 内容在远程 www 子目录)
|
||
local sub
|
||
if [[ "$rel" == dashboard/* ]]; then
|
||
sub="${rel#dashboard/}"
|
||
elif [[ "$rel" == www/* ]]; then
|
||
sub="$rel"
|
||
else
|
||
warn "路径不在 dashboard/ 或 www/ 下,跳过: $item"
|
||
return 1
|
||
fi
|
||
local remote_path="${REMOTE_ROOT}/${sub}"
|
||
|
||
if [[ ! -e "$local_full" ]]; then
|
||
warn "路径不存在: ${local_full}"
|
||
return 1
|
||
fi
|
||
|
||
local rsync_opts=(-avz --progress)
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
rsync_opts+=(-n)
|
||
fi
|
||
# dashboard 前端 dist 同步默认跳过 uploads/(服务器挂载的上传目录,避免 --delete 误删)
|
||
if [[ "$sub" == "frontend/dist" ]]; then
|
||
rsync_opts+=(--exclude '/uploads/')
|
||
fi
|
||
|
||
if [[ -d "$local_full" ]]; then
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
info "预览目录: ${rel}/ -> ${SERVER}:${remote_path}/"
|
||
else
|
||
info "同步目录: ${rel}/ -> ${SERVER}:${remote_path}/"
|
||
fi
|
||
rsync "${rsync_opts[@]}" --delete \
|
||
-e ssh \
|
||
"${local_full}/" "${SERVER}:${remote_path}/"
|
||
else
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
info "预览文件: ${rel} -> ${SERVER}:${remote_path}"
|
||
else
|
||
info "同步文件: ${rel} -> ${SERVER}:${remote_path}"
|
||
ssh "$SERVER" "mkdir -p $(dirname "$remote_path")"
|
||
fi
|
||
rsync "${rsync_opts[@]}" \
|
||
-e ssh \
|
||
"$local_full" "${SERVER}:${remote_path}"
|
||
fi
|
||
}
|
||
|
||
sync_paths() {
|
||
local paths=("$@")
|
||
local item
|
||
|
||
cd "$PROJECT_ROOT"
|
||
|
||
for item in "${paths[@]}"; do
|
||
rsync_item "$item"
|
||
done
|
||
}
|
||
|
||
DRY_RUN=false
|
||
ARGS=()
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
-n)
|
||
DRY_RUN=true
|
||
;;
|
||
-h|--help)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
*)
|
||
ARGS+=("$arg")
|
||
;;
|
||
esac
|
||
done
|
||
|
||
PATHS=()
|
||
if [[ ${#ARGS[@]} -eq 0 ]]; then
|
||
PATHS=("${DEFAULT_PATHS[@]}")
|
||
else
|
||
for arg in "${ARGS[@]}"; do
|
||
while IFS= read -r path; do
|
||
PATHS+=("$path")
|
||
done < <(resolve_preset_paths "$arg")
|
||
done
|
||
fi
|
||
|
||
# 若包含 www .output 产物,先校验本地已构建
|
||
for p in "${PATHS[@]}"; do
|
||
rel="${p#./}"
|
||
if [[ "$rel" == www/.output || "$rel" == www/.output/* ]]; then
|
||
ensure_www_built
|
||
break
|
||
fi
|
||
done
|
||
|
||
if [[ "$DRY_RUN" = true ]]; then
|
||
info "虚拟同步(dashboard -> ${SERVER}:${REMOTE_ROOT}/ ;www -> ${SERVER}:${WWW_REMOTE_ROOT}/)"
|
||
else
|
||
info "同步(dashboard -> ${SERVER}:${REMOTE_ROOT}/ ;www -> ${SERVER}:${WWW_REMOTE_ROOT}/)"
|
||
fi
|
||
|
||
sync_paths "${PATHS[@]}"
|
||
|
||
# www 部署后续提示
|
||
for p in "${PATHS[@]}"; do
|
||
rel="${p#./}"
|
||
if [[ "$rel" == www/* ]]; then
|
||
echo ""
|
||
ok "www 产物已上传,下一步(在服务器上执行):"
|
||
echo " cd ${WWW_REMOTE_ROOT} && docker compose up -d"
|
||
break
|
||
fi
|
||
done
|
||
|
||
echo ""
|
||
ok "完成!"
|