2026-06-09 19:44:51 +08:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
SERVER="ubuntu@ec2-3-71-153-45.eu-central-1.compute.amazonaws.com"
|
|
|
|
|
|
REMOTE_ROOT="/data/project/dashboard"
|
|
|
|
|
|
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
|
|
|
|
SSH_KEY="$HOME/.ssh/aws_eafon.pem"
|
2026-06-16 19:15:02 +08:00
|
|
|
|
SSH_OPTS=(-i "$SSH_KEY" -o StrictHostKeyChecking=no)
|
|
|
|
|
|
|
|
|
|
|
|
FRONTEND_PATHS=(
|
|
|
|
|
|
./frontend/dist/
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
BACKEND_PATHS=(
|
|
|
|
|
|
./backend/src/
|
|
|
|
|
|
./backend/package.json
|
|
|
|
|
|
./backend/pnpm-lock.yaml
|
|
|
|
|
|
)
|
2026-06-09 19:44:51 +08:00
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
COMPOSE_PATHS=(
|
|
|
|
|
|
./docker-compose.yml
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_PATHS=(
|
|
|
|
|
|
"${FRONTEND_PATHS[@]}"
|
|
|
|
|
|
"${BACKEND_PATHS[@]}"
|
|
|
|
|
|
"${COMPOSE_PATHS[@]}"
|
|
|
|
|
|
)
|
2026-06-09 19:44:51 +08:00
|
|
|
|
|
|
|
|
|
|
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"; }
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
show_help() {
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
|
用法: $(basename "$0") [选项] [目标|路径...]
|
|
|
|
|
|
|
|
|
|
|
|
将本地代码同步到远程服务器(rsync)。
|
|
|
|
|
|
|
|
|
|
|
|
远程: ${SERVER}:${REMOTE_ROOT}/
|
|
|
|
|
|
|
|
|
|
|
|
选项:
|
|
|
|
|
|
-n 虚拟执行,不实际传输或删除远程文件
|
|
|
|
|
|
-h, --help 显示此帮助信息
|
|
|
|
|
|
|
|
|
|
|
|
目标:
|
|
|
|
|
|
frontend, f 同步前端 dist/(不含 nginx.conf)
|
|
|
|
|
|
backend, b 同步后端 src/、package.json、pnpm-lock.yaml(不含 Dockerfile)
|
|
|
|
|
|
compose, c 同步 docker-compose.yml
|
|
|
|
|
|
all, a 同步以上全部(默认)
|
|
|
|
|
|
(路径) 同步指定文件或目录,可传多个
|
|
|
|
|
|
|
|
|
|
|
|
默认不同步 nginx.conf 与 Dockerfile,需要时请显式指定,例如:
|
|
|
|
|
|
./frontend/nginx.conf
|
|
|
|
|
|
./backend/Dockerfile
|
|
|
|
|
|
|
|
|
|
|
|
指定路径时,会映射到服务器对应位置,例如:
|
|
|
|
|
|
./frontend/dist/assets -> ${REMOTE_ROOT}/frontend/dist/assets
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
$(basename "$0") 同步默认内容到服务器
|
|
|
|
|
|
$(basename "$0") frontend 仅同步前端
|
|
|
|
|
|
$(basename "$0") backend compose 同步后端与 docker-compose
|
|
|
|
|
|
$(basename "$0") ./frontend/nginx.conf 单独同步 nginx.conf
|
|
|
|
|
|
$(basename "$0") ./backend/Dockerfile 单独同步 Dockerfile
|
|
|
|
|
|
$(basename "$0") ./frontend/dist/assets 同步指定目录
|
|
|
|
|
|
$(basename "$0") -n 虚拟执行,预览全部同步
|
|
|
|
|
|
$(basename "$0") -n frontend 虚拟执行,预览前端同步
|
|
|
|
|
|
EOF
|
2026-06-09 19:44:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
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[@]}" ;;
|
|
|
|
|
|
all|a) printf '%s\n' "${DEFAULT_PATHS[@]}" ;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
echo "$preset"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
2026-06-09 19:44:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
rsync_item() {
|
|
|
|
|
|
local item="$1"
|
|
|
|
|
|
local rel remote_path local_full
|
|
|
|
|
|
|
|
|
|
|
|
if [[ "$item" == /* ]]; then
|
|
|
|
|
|
if [[ "$item" == "$PROJECT_ROOT"/* ]]; then
|
|
|
|
|
|
rel="${item#"$PROJECT_ROOT"/}"
|
|
|
|
|
|
local_full="$item"
|
|
|
|
|
|
else
|
|
|
|
|
|
warn "路径不在项目内,跳过: $item"
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
2026-06-10 10:53:51 +08:00
|
|
|
|
else
|
2026-06-16 19:15:02 +08:00
|
|
|
|
rel="${item#./}"
|
|
|
|
|
|
rel="${rel%/}"
|
|
|
|
|
|
local_full="${PROJECT_ROOT}/${rel}"
|
2026-06-10 10:53:51 +08:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
remote_path="${REMOTE_ROOT}/${rel}"
|
2026-06-09 19:44:51 +08:00
|
|
|
|
|
|
|
|
|
|
if [[ ! -e "$local_full" ]]; then
|
|
|
|
|
|
warn "路径不存在: ${local_full}"
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
local rsync_opts=(-avz --progress)
|
|
|
|
|
|
if [[ "$DRY_RUN" = true ]]; then
|
|
|
|
|
|
rsync_opts+=(-n)
|
|
|
|
|
|
fi
|
2026-06-09 19:44:51 +08:00
|
|
|
|
|
|
|
|
|
|
if [[ -d "$local_full" ]]; then
|
2026-06-16 19:15:02 +08:00
|
|
|
|
if [[ "$DRY_RUN" = true ]]; then
|
|
|
|
|
|
info "预览目录: ${rel}/ -> ${SERVER}:${remote_path}/"
|
|
|
|
|
|
else
|
|
|
|
|
|
info "同步目录: ${rel}/ -> ${SERVER}:${remote_path}/"
|
|
|
|
|
|
fi
|
|
|
|
|
|
rsync "${rsync_opts[@]}" --delete \
|
|
|
|
|
|
-e "ssh ${SSH_OPTS[*]}" \
|
|
|
|
|
|
"${local_full}/" "${SERVER}:${remote_path}/"
|
2026-06-09 19:44:51 +08:00
|
|
|
|
else
|
2026-06-16 19:15:02 +08:00
|
|
|
|
if [[ "$DRY_RUN" = true ]]; then
|
|
|
|
|
|
info "预览文件: ${rel} -> ${SERVER}:${remote_path}"
|
|
|
|
|
|
else
|
|
|
|
|
|
info "同步文件: ${rel} -> ${SERVER}:${remote_path}"
|
|
|
|
|
|
ssh "${SSH_OPTS[@]}" "$SERVER" "mkdir -p $(dirname "$remote_path")"
|
|
|
|
|
|
fi
|
|
|
|
|
|
rsync "${rsync_opts[@]}" \
|
|
|
|
|
|
-e "ssh ${SSH_OPTS[*]}" \
|
|
|
|
|
|
"$local_full" "${SERVER}:${remote_path}"
|
2026-06-09 19:44:51 +08:00
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
sync_paths() {
|
|
|
|
|
|
local paths=("$@")
|
|
|
|
|
|
local item
|
2026-06-09 19:44:51 +08:00
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
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[@]}")
|
2026-06-09 19:44:51 +08:00
|
|
|
|
else
|
2026-06-16 19:15:02 +08:00
|
|
|
|
for arg in "${ARGS[@]}"; do
|
|
|
|
|
|
while IFS= read -r path; do
|
|
|
|
|
|
PATHS+=("$path")
|
|
|
|
|
|
done < <(resolve_preset_paths "$arg")
|
2026-06-09 19:44:51 +08:00
|
|
|
|
done
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-06-16 19:15:02 +08:00
|
|
|
|
if [[ "$DRY_RUN" = true ]]; then
|
|
|
|
|
|
info "虚拟同步至 ${SERVER}:${REMOTE_ROOT}/"
|
|
|
|
|
|
else
|
|
|
|
|
|
info "同步至 ${SERVER}:${REMOTE_ROOT}/"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
sync_paths "${PATHS[@]}"
|
|
|
|
|
|
|
2026-06-09 19:44:51 +08:00
|
|
|
|
echo ""
|
2026-06-16 19:15:02 +08:00
|
|
|
|
ok "完成!"
|