优化项目结构
This commit is contained in:
Executable
+252
@@ -0,0 +1,252 @@
|
||||
#!/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 "完成!"
|
||||
Reference in New Issue
Block a user