fix(deploy): bash 3.2 compat, sshpass auth, sync admin dist to public/admin

- Replace bash 4.2+ printf %(...)T with date subshell so script runs on
  the macOS-default bash 3.2.
- Add SSH_PASS_FILE / SSH_PASSWORD support via sshpass for environments
  where password auth is the only path.
- After admin npm build, rsync view/admin/dist/ into public/admin/
  (preserving UEditor and favicon) so the project rsync ships the
  admin to the path nginx actually serves from.
- Simplify rsync exclude list to drop view/admin/ wholesale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
danaisuiyuan
2026-05-10 15:47:24 +08:00
parent b1882f1f11
commit 9559031536
3 changed files with 40 additions and 14 deletions

View File

@@ -38,8 +38,30 @@ log() { printf "\033[1;36m[%s]\033[0m %s\n" "$(_ts)" "$*"; }
warn() { printf "\033[1;33m[%s] WARN\033[0m %s\n" "$(_ts)" "$*"; } warn() { printf "\033[1;33m[%s] WARN\033[0m %s\n" "$(_ts)" "$*"; }
err() { printf "\033[1;31m[%s] ERR\033[0m %s\n" "$(_ts)" "$*" >&2; } err() { printf "\033[1;31m[%s] ERR\033[0m %s\n" "$(_ts)" "$*" >&2; }
ssh_run() { ssh -p "$SSH_PORT" "${SSH_USER}@${SSH_HOST}" "$@"; } # 认证:优先 SSH_PASS_FILEsshpass -f其次 SSH_PASSWORD env再退回公钥
ssh_pipe() { ssh -p "$SSH_PORT" "${SSH_USER}@${SSH_HOST}" bash -se; } SSH_BASE=(ssh -p "$SSH_PORT" -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR)
if [ -n "${SSH_PASS_FILE:-}" ] && [ -f "${SSH_PASS_FILE/#~/$HOME}" ]; then
SSH_PASS_FILE="${SSH_PASS_FILE/#~/$HOME}"
[ "$(stat -f '%A' "$SSH_PASS_FILE" 2>/dev/null || stat -c '%a' "$SSH_PASS_FILE")" = "600" ] || \
warn "SSH_PASS_FILE permission is not 600 (consider: chmod 600 $SSH_PASS_FILE)"
command -v sshpass >/dev/null || { err "sshpass not installed (brew install hudochenkov/sshpass/sshpass)"; exit 1; }
SSH_AUTH=(sshpass -f "$SSH_PASS_FILE")
elif [ -n "${SSH_PASSWORD:-}" ]; then
command -v sshpass >/dev/null || { err "sshpass not installed"; exit 1; }
SSH_AUTH=(sshpass -e) # reads SSHPASS env
export SSHPASS="$SSH_PASSWORD"
else
SSH_AUTH=()
fi
ssh_run() { "${SSH_AUTH[@]}" "${SSH_BASE[@]}" "${SSH_USER}@${SSH_HOST}" "$@"; }
ssh_pipe() { "${SSH_AUTH[@]}" "${SSH_BASE[@]}" "${SSH_USER}@${SSH_HOST}" bash -se; }
# rsync 走相同认证
if [ -n "${SSH_AUTH+x}" ] && [ "${#SSH_AUTH[@]}" -gt 0 ]; then
RSYNC_RSH="${SSH_AUTH[*]} ssh -p $SSH_PORT -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
else
RSYNC_RSH="ssh -p $SSH_PORT"
fi
check_health() { check_health() {
local url code i local url code i
@@ -156,6 +178,14 @@ else
fi fi
[ -d "$PROJECT_ROOT/view/admin/dist" ] || { err "view/admin/dist missing; build failed?"; exit 1; } [ -d "$PROJECT_ROOT/view/admin/dist" ] || { err "view/admin/dist missing; build failed?"; exit 1; }
# admin 实际运行目录 = pro_v3.5.1/public/admin/(远端 nginx 指向同名路径)
# 把 build 产物同步到 public/admin/,再让项目根 rsync 整体推上去
log "Sync admin dist -> public/admin/"
mkdir -p "$PROJECT_ROOT/public/admin"
rsync -a --delete \
--exclude='UEditor/' --exclude='favicon.ico' \
"$PROJECT_ROOT/view/admin/dist/" "$PROJECT_ROOT/public/admin/"
# ---- VERSION ---- # ---- VERSION ----
TS=$(date +%Y%m%d-%H%M%S) TS=$(date +%Y%m%d-%H%M%S)
SHA=$(git -C "$PROJECT_ROOT/.." rev-parse --short HEAD 2>/dev/null || echo "nogit") SHA=$(git -C "$PROJECT_ROOT/.." rev-parse --short HEAD 2>/dev/null || echo "nogit")
@@ -166,7 +196,7 @@ log "Release tag: $TAG"
RSYNC_OPTS=(-az --delete --human-readable RSYNC_OPTS=(-az --delete --human-readable
--exclude-from="$EXCLUDE_FILE" --exclude-from="$EXCLUDE_FILE"
--backup --backup-dir="$REMOTE_BAK/$TAG" --backup --backup-dir="$REMOTE_BAK/$TAG"
-e "ssh -p $SSH_PORT") -e "$RSYNC_RSH")
[ "$DRY_RUN" -eq 1 ] && RSYNC_OPTS+=(--dry-run --itemize-changes) [ "$DRY_RUN" -eq 1 ] && RSYNC_OPTS+=(--dry-run --itemize-changes)
log "rsync -> ${SSH_USER}@${SSH_HOST}:$REMOTE_ROOT (backup: $REMOTE_BAK/$TAG)" log "rsync -> ${SSH_USER}@${SSH_HOST}:$REMOTE_ROOT (backup: $REMOTE_BAK/$TAG)"

View File

@@ -12,15 +12,5 @@ public/uploads/
.env-huangjinfen .env-huangjinfen
view/uniapp/ view/uniapp/
view/uniapp_v2/ view/uniapp_v2/
view/admin/node_modules/ view/admin/
view/admin/src/
view/admin/public/
view/admin/.env*
view/admin/package*.json
view/admin/babel.config.js
view/admin/vue.config.js
view/admin/jest.config.js
view/admin/alias.config.js
view/admin/tests/
view/admin/dist/.DS_Store
deploy/.last-release deploy/.last-release

View File

@@ -7,6 +7,12 @@ SSH_PORT=22
REMOTE_ROOT=/www/wwwroot/syj.fsgx.cn REMOTE_ROOT=/www/wwwroot/syj.fsgx.cn
REMOTE_BAK=/www/wwwroot/syj.fsgx.cn-bak REMOTE_BAK=/www/wwwroot/syj.fsgx.cn-bak
# 密码认证(可选):把 SSH 密码放在仓库外的 600 文件里,例如 ~/.ssh/syj-deploy.pass
# 优先级SSH_PASS_FILE > $SSH_PASSWORD env > 公钥认证
# 文件方式(推荐):
# echo 'YOUR_PASSWORD' > ~/.ssh/syj-deploy.pass && chmod 600 ~/.ssh/syj-deploy.pass
SSH_PASS_FILE=~/.ssh/syj-deploy.pass
# 健康检查 URL任一非 200 即触发回滚 # 健康检查 URL任一非 200 即触发回滚
HEALTH_URLS=( HEALTH_URLS=(
"https://syj.fsgx.cn/api/version" "https://syj.fsgx.cn/api/version"