You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
9.3 KiB
221 lines
9.3 KiB
#!/usr/bin/env bash
|
|
# 一键发布测试(dev)环境 —— 把 app / console / admin 依次发到同一家公司的同一台测试机。
|
|
#
|
|
# 本脚本不重复任何部署逻辑,只是按顺序调用各服务自己的 deploy/<服务>/dev-deploy.sh <环境>,
|
|
# 再把结果汇总成一张表。所以「构建什么镜像、推到哪个仓库、远端怎么重启」的规则仍然只有一处
|
|
# (各服务的 dev-deploy.sh + deploy/registry-profiles.sh),改那边这里自动跟随。
|
|
#
|
|
# 用法(在 deploy/ 下):
|
|
# ./dev-deploy-all.sh # 交互选公司(环境),发布全部三个服务
|
|
# ./dev-deploy-all.sh along # 发布全部服务到阿龙测试环境
|
|
# ./dev-deploy-all.sh yunyan app admin # 只发 app 和 admin 到云雁测试环境
|
|
# ./dev-deploy-all.sh along -y -k # 不确认、失败也继续发后面的服务
|
|
#
|
|
# 发布顺序固定 app → console → admin:admin 的页面要连 console,console 又依赖业务库,
|
|
# 让后端先就位,刷新页面时不至于打到还没起来的后端。
|
|
#
|
|
# ⚠️ macOS 自带 bash 3.2 不认多字节字符边界,`"$rc)"` 会被当成变量名 `rc)` 而在 set -u 下
|
|
# 报 unbound variable。所以本文件里凡是变量后面紧跟中文标点的,一律写成 ${rc}。
|
|
set -euo pipefail
|
|
|
|
DEPLOY_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # 仓库的 deploy/
|
|
source "$DEPLOY_ROOT/registry-profiles.sh" # 公司 → 仓库地址/账号
|
|
|
|
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
|
|
green() { printf "\033[32m%s\033[0m\n" "$*"; }
|
|
yellow() { printf "\033[33m%s\033[0m\n" "$*"; }
|
|
red() { printf "\033[31m%s\033[0m\n" "$*"; }
|
|
|
|
# 可选公司(测试环境)。名字与各服务 dev-deploy.sh 的 ENVS 一致,脚本启动时会交叉校验,
|
|
# 一边加了新环境另一边忘了加时会直接报错,不会发到一半才炸。
|
|
ENVS=(lingpu yunyan along)
|
|
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时用它
|
|
ALL_SVCS=(app console admin) # 默认发布顺序
|
|
|
|
usage() {
|
|
cat >&2 <<EOF
|
|
用法: ./dev-deploy-all.sh [公司/环境] [服务...] [选项] (在 deploy/ 下运行)
|
|
|
|
公司/环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV})
|
|
服务 : ${ALL_SVCS[*]// /, } 的任意组合(省略 = 全部,按 ${ALL_SVCS[*]} 顺序)
|
|
|
|
选项:
|
|
-y, --yes 跳过发布前确认
|
|
-k, --keep-going 某个服务失败后继续发布其余服务(默认遇错即停)
|
|
-n, --dry-run 只打印将要执行的命令,不构建也不连服务器
|
|
-h, --help 看本帮助
|
|
|
|
例:
|
|
./dev-deploy-all.sh along # 全量发到阿龙测试环境
|
|
./dev-deploy-all.sh yunyan console # 只把 console 发到云雁测试环境
|
|
EOF
|
|
}
|
|
|
|
# ── 参数解析:位置参数里第一个能对上 ENVS 的当环境,其余当服务名 ──
|
|
DEV_ENV=""
|
|
SVCS=()
|
|
ASSUME_YES=0
|
|
KEEP_GOING=0
|
|
DRY_RUN=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
-h|--help|help) usage; exit 0 ;;
|
|
-y|--yes) ASSUME_YES=1 ;;
|
|
-k|--keep-going) KEEP_GOING=1 ;;
|
|
-n|--dry-run) DRY_RUN=1 ;;
|
|
-*) red "✗ 未知选项: $arg"; usage; exit 1 ;;
|
|
*)
|
|
matched=0
|
|
for e in "${ENVS[@]}"; do [ "$arg" = "$e" ] && matched=1 && break; done
|
|
if [ "$matched" = 1 ] && [ -z "$DEV_ENV" ]; then
|
|
DEV_ENV="$arg"
|
|
continue
|
|
fi
|
|
matched=0
|
|
for s in "${ALL_SVCS[@]}"; do [ "$arg" = "$s" ] && matched=1 && break; done
|
|
if [ "$matched" = 1 ]; then
|
|
SVCS+=("$arg")
|
|
else
|
|
red "✗ 无法识别的参数: ${arg}(公司应为 ${ENVS[*]// /|},服务应为 ${ALL_SVCS[*]// /|})"
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ── 未指定公司 → 交互选择(菜单顺带显示仓库,选错一眼能看出来)──
|
|
if [ -z "$DEV_ENV" ]; then
|
|
if [ -t 0 ]; then
|
|
blue "选择要发布的公司(测试环境):"
|
|
i=1
|
|
for e in "${ENVS[@]}"; do
|
|
( registry_profile "$e" >/dev/null 2>&1
|
|
printf " %d) %-8s → %-6s 仓库 %s\n" "$i" "$e" "${COMPANY_DESC:-}" "${REGISTRY:-}" >&2 )
|
|
i=$((i+1))
|
|
done
|
|
printf " 请输入序号 [1-%d]: " "${#ENVS[@]}" >&2
|
|
read -r sel || { red "✗ 已取消"; exit 1; }
|
|
case "$sel" in ''|*[!0-9]*) red "✗ 无效序号: $sel"; exit 1 ;; esac
|
|
{ [ "$sel" -ge 1 ] && [ "$sel" -le "${#ENVS[@]}" ]; } || { red "✗ 序号超出范围"; exit 1; }
|
|
DEV_ENV="${ENVS[$((sel-1))]}"
|
|
else
|
|
DEV_ENV="$DEFAULT_ENV"
|
|
fi
|
|
fi
|
|
|
|
# 服务列表一律按 ALL_SVCS 的顺序重排并去重:命令行写成 `admin app` 也照样先发后端,
|
|
# 顺序是依赖关系决定的,不该由参数书写顺序决定。
|
|
if [ "${#SVCS[@]}" -gt 0 ]; then
|
|
ORDERED=()
|
|
for s in "${ALL_SVCS[@]}"; do
|
|
for picked in "${SVCS[@]}"; do
|
|
if [ "$s" = "$picked" ]; then ORDERED+=("$s"); break; fi
|
|
done
|
|
done
|
|
SVCS=("${ORDERED[@]}")
|
|
else
|
|
SVCS=("${ALL_SVCS[@]}")
|
|
fi
|
|
|
|
registry_profile "$DEV_ENV" || exit 1
|
|
|
|
# ── 前置校验:脚本在、可执行、且该服务确实认识这个环境 ──
|
|
# 各服务的 ENVS 是各自 dev-deploy.sh 里的数组,这里读出来比对,避免「顶层能选、子脚本不认」
|
|
# 的漂移在推完镜像之后才暴露。
|
|
sub_envs() { sed -n 's/^ENVS=(\([^)]*\)).*/\1/p' "$DEPLOY_ROOT/$1/dev-deploy.sh"; }
|
|
sub_host() { # 仅用于确认页展示,解析不出来就留空
|
|
sed -n "/^ $2)/,/;;/p" "$DEPLOY_ROOT/$1/dev-deploy.sh" |
|
|
sed -n 's/.*DEV_HOST="\([^"]*\)".*/\1/p' | head -1
|
|
}
|
|
for svc in "${SVCS[@]}"; do
|
|
script="$DEPLOY_ROOT/$svc/dev-deploy.sh"
|
|
[ -f "$script" ] || { red "✗ 找不到 $script"; exit 1; }
|
|
found=0
|
|
for e in $(sub_envs "$svc"); do [ "$e" = "$DEV_ENV" ] && found=1 && break; done
|
|
[ "$found" = 1 ] || { red "✗ $svc/dev-deploy.sh 的 ENVS 里没有环境 ${DEV_ENV},请先在该脚本的 env_profile 补一段"; exit 1; }
|
|
done
|
|
|
|
# Docker 得先能用。子脚本是「docker login → build」的顺序,login 走凭据存储不碰 daemon,
|
|
# 所以 daemon 没起时会先打出一句 Login Succeeded 再失败,看着像是登录出了问题——在这里先拦一道。
|
|
if [ "$DRY_RUN" != 1 ]; then
|
|
command -v docker >/dev/null 2>&1 || { red "✗ 没找到 docker 命令,请先装 Docker"; exit 1; }
|
|
if ! docker info >/dev/null 2>&1; then
|
|
red "✗ Docker 守护进程没在运行,无法构建镜像"
|
|
echo " macOS: open -a Docker (等托盘鲸鱼图标停止转动后重跑本脚本)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ── 确认 ──
|
|
blue "═══ 一键发布测试环境 ═══"
|
|
printf " 公司/环境 : %s(%s)\n" "$DEV_ENV" "${COMPANY_DESC:-}"
|
|
printf " 镜像仓库 : %s(账号 %s)\n" "${REGISTRY:-}" "${REGISTRY_USER:-}"
|
|
printf " 服务顺序 : %s\n" "${SVCS[*]}"
|
|
for svc in "${SVCS[@]}"; do
|
|
printf " - %-8s → %s\n" "$svc" "$(sub_host "$svc" "$DEV_ENV" || true)"
|
|
done
|
|
printf " 镜像 tag : dev-latest(每次覆盖同一 tag)\n"
|
|
if [ "$DRY_RUN" = 1 ]; then yellow " (dry-run:只打印命令,不构建、不连服务器)"; fi
|
|
if [ "$DRY_RUN" != 1 ] && [ "$ASSUME_YES" != 1 ] && [ -t 0 ]; then
|
|
printf " 确认发布?[y/N]: "
|
|
read -r ok || { red "✗ 已取消"; exit 1; }
|
|
case "$ok" in y|Y|yes|YES) ;; *) yellow "已取消"; exit 0 ;; esac
|
|
fi
|
|
|
|
# ── 依次发布,记录每个服务的结果与耗时 ──
|
|
# 子脚本在「服务器还没有真实 .env」时会打印提示并以 0 退出(这是它的正常分支,不是失败),
|
|
# 一键发布里这种情况必须显出来,否则一屏日志刷过去,你会以为三个服务都发上去了。
|
|
LOG_DIR="${TMPDIR:-/tmp}/starpivot-dev-deploy-$$"
|
|
mkdir -p "$LOG_DIR"
|
|
RESULTS=()
|
|
FAILED=0
|
|
for svc in "${SVCS[@]}"; do
|
|
blue "───────────────────────────────────────────────"
|
|
blue ">>> [$svc] 发布到 $DEV_ENV"
|
|
start=$(date +%s)
|
|
log="$LOG_DIR/$svc.log"
|
|
if [ "$DRY_RUN" = 1 ]; then
|
|
echo " bash $DEPLOY_ROOT/$svc/dev-deploy.sh $DEV_ENV"
|
|
: > "$log"
|
|
rc=0
|
|
else
|
|
set +e
|
|
bash "$DEPLOY_ROOT/$svc/dev-deploy.sh" "$DEV_ENV" 2>&1 | tee "$log"
|
|
rc=${PIPESTATUS[0]}
|
|
set -e
|
|
fi
|
|
cost=$(( $(date +%s) - start ))
|
|
if [ "$rc" -ne 0 ]; then
|
|
RESULTS+=("$svc|失败(退出码 $rc)|${cost}s")
|
|
FAILED=1
|
|
red "✗ [$svc] 发布失败(退出码 ${rc})"
|
|
if [ "$KEEP_GOING" != 1 ]; then
|
|
red " 已中止后续服务(要继续请加 -k/--keep-going)"
|
|
break
|
|
fi
|
|
elif grep -q "缺少真实 .env" "$log"; then
|
|
RESULTS+=("$svc|跳过(服务器缺 .env)|${cost}s")
|
|
yellow "⚠ [$svc] 镜像已推送,但服务器缺 .env,未重启容器"
|
|
else
|
|
RESULTS+=("$svc|成功|${cost}s")
|
|
green "✓ [$svc] 完成(${cost}s)"
|
|
fi
|
|
done
|
|
|
|
# ── 汇总 ──
|
|
blue "═══ 发布结果($DEV_ENV / ${COMPANY_DESC:-})═══"
|
|
for r in "${RESULTS[@]}"; do
|
|
svc="${r%%|*}"; rest="${r#*|}"; status="${rest%%|*}"; cost="${rest##*|}"
|
|
case "$status" in
|
|
成功) printf " \033[32m✓\033[0m %-8s %-22s %s\n" "$svc" "$status" "$cost" ;;
|
|
跳过*) printf " \033[33m⚠\033[0m %-8s %-22s %s\n" "$svc" "$status" "$cost" ;;
|
|
*) printf " \033[31m✗\033[0m %-8s %-22s %s\n" "$svc" "$status" "$cost" ;;
|
|
esac
|
|
done
|
|
echo " 日志: $LOG_DIR"
|
|
|
|
if [ "$FAILED" = 1 ]; then
|
|
red "✗ 有服务发布失败"
|
|
exit 1
|
|
fi
|
|
green "✓ 全部完成"
|
|
|