#!/usr/bin/env bash set -Eeuo pipefail # 正式环境部署参数固定在脚本中,避免通过环境变量误传到测试或其他服务器。 readonly ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" readonly BUILD_DIR="${ROOT_DIR}/shihezi" readonly DEPLOY_HOST="47.111.103.66" readonly DEPLOY_USER="root" readonly DEPLOY_SSH_PORT="22" readonly DEPLOY_PATH="/opt/service/project/shz-admin/dist" DRY_RUN=0 info() { printf '\033[1;32m%s\033[0m\n' "$*" } error() { printf '\033[1;31m%s\033[0m\n' "$*" >&2 } die() { error "$*" exit 1 } usage() { cat <<'EOF' 用法: ./scripts/deploy-production-frontend.sh ./scripts/deploy-production-frontend.sh --dry-run 功能: 1. 使用 npm run build 构建当前 PC 前端 2. 将 shihezi/ 上传到正式服务器的 Nginx 静态目录 3. 校验远程 index.html,并比对本地与远程 SHA-256 正式环境参数(固定): SSH 主机:root@47.111.103.66:22 静态目录:/opt/service/project/shz-admin/dist 访问前缀:/shihezi/ 参数: --dry-run 只构建并检查产物,不连接服务器、不上传文件 -h, --help 显示帮助 说明: 脚本不会自动安装依赖。若 node_modules 不存在,请先执行 npm install。 上传使用 rsync --delete,远程静态目录中本地没有的文件会被删除。 脚本使用当前用户已有的 SSH key/agent,不保存密码。 EOF } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=1 ;; -h|--help) usage exit 0 ;; *) die "未知参数:$1(使用 --help 查看用法)" ;; esac shift done } validate_local_environment() { command -v npm >/dev/null 2>&1 || die "未找到 npm" command -v ssh >/dev/null 2>&1 || die "未找到 ssh" command -v rsync >/dev/null 2>&1 || die "未找到 rsync" command -v shasum >/dev/null 2>&1 || die "未找到 shasum,无法校验构建产物" [[ -f "${ROOT_DIR}/package.json" ]] || die "未找到 ${ROOT_DIR}/package.json" [[ -d "${ROOT_DIR}/node_modules" ]] || die "未找到 node_modules,请先执行 npm install" } build_frontend() { info "开始构建正式前端:npm run build" npm run build [[ -s "${BUILD_DIR}/index.html" ]] || die "构建完成但未找到 ${BUILD_DIR}/index.html" info "本地构建完成:${BUILD_DIR}" } check_production_access() { local target="${DEPLOY_USER}@${DEPLOY_HOST}" info "检查正式服务器目录:${target}:${DEPLOY_PATH}/" ssh -o BatchMode=yes -o ConnectTimeout=10 -p "${DEPLOY_SSH_PORT}" "${target}" \ "test -d '${DEPLOY_PATH}' && test -w '${DEPLOY_PATH}'" } upload_frontend() { local target="${DEPLOY_USER}@${DEPLOY_HOST}" local ssh_command="ssh -o BatchMode=yes -o ConnectTimeout=10 -p ${DEPLOY_SSH_PORT}" if [[ "${DRY_RUN}" == "1" ]]; then info "DRY RUN:不会连接服务器或上传文件" info "目标:${target}:${DEPLOY_PATH}/" return fi check_production_access info "上传构建产物:${BUILD_DIR}/ -> ${target}:${DEPLOY_PATH}/" rsync -az --delete --delay-updates \ -e "${ssh_command}" \ "${BUILD_DIR}/" "${target}:${DEPLOY_PATH}/" local local_hash local remote_hash local_hash="$(shasum -a 256 "${BUILD_DIR}/index.html" | awk '{print $1}')" remote_hash="$(ssh -o BatchMode=yes -o ConnectTimeout=10 -p "${DEPLOY_SSH_PORT}" "${target}" \ "test -s '${DEPLOY_PATH}/index.html' && sha256sum '${DEPLOY_PATH}/index.html' | awk '{print \$1}'")" [[ -n "${remote_hash}" ]] || die "远程 index.html 不存在或为空" [[ "${local_hash}" == "${remote_hash}" ]] || die "远程 index.html 校验失败:本地 ${local_hash},远程 ${remote_hash}" info "正式前端部署完成:http://${DEPLOY_HOST}/shihezi/" info "index.html SHA-256 校验通过:${local_hash}" } main() { parse_args "$@" cd "${ROOT_DIR}" validate_local_environment build_frontend upload_frontend } main "$@"