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.
152 lines
9.4 KiB
152 lines
9.4 KiB
#!/usr/bin/env bash
|
|
# app 开发环境:构建开发镜像 → 推送 → 远端拉取重启。
|
|
#
|
|
# 业务五服务(gateway/home/api/mcp/timer)打成单镜像 starpivot-app,推到私有仓库后远端拉取重启。
|
|
# 仅做镜像部署:服务器上的 docker-compose.yml / .env / confs/ 由你自行维护
|
|
# (照着仓库里的 env/env.example、confs/*.yaml.example 在服务器上放好真实文件),本脚本不上传任何配置。
|
|
#
|
|
# 用法(在 deploy/app/ 下):
|
|
# ./dev-deploy.sh lingpu # 发布到灵谱测试环境
|
|
# ./dev-deploy.sh yunyan # 发布到云雁测试环境
|
|
# ./dev-deploy.sh along # 发布到阿龙测试环境
|
|
# ./dev-deploy.sh # 不带参数 → 交互选择环境(无 TTY 时用默认环境 lingpu)
|
|
#
|
|
# 参数只用于选环境;开发版镜像始终使用固定 tag dev-latest,每次构建覆盖同一 tag。
|
|
set -euo pipefail
|
|
|
|
SVC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # deploy/app
|
|
REPO_ROOT="$(cd "$SVC_DIR/../.." && pwd)" # 仓库根
|
|
BUILD_CONTEXT="$REPO_ROOT/apps/services" # Go 构建上下文
|
|
DOCKERFILE="$BUILD_CONTEXT/Dockerfile.voitrans"
|
|
|
|
# 各公司镜像仓库地址/账号统一放这里,换仓库只改 deploy/registry-profiles.sh
|
|
source "$REPO_ROOT/deploy/registry-profiles.sh"
|
|
|
|
blue() { printf "\033[34m%s\033[0m\n" "$*"; }
|
|
green() { printf "\033[32m%s\033[0m\n" "$*"; }
|
|
red() { printf "\033[31m%s\033[0m\n" "$*"; }
|
|
|
|
IMAGE_NAME="starpivot-app" # 业务单镜像名
|
|
|
|
# ╔══════════════════════════════════════════════════════════════════════╗
|
|
# ║ 各测试环境部署目标 / 私有仓库凭据 —— 每环境一整套 仓库/HOST/KEY/远端目录。 ║
|
|
# ║ 写死在此(本文件纳入 git,私有仓库内可接受)。 ║
|
|
# ║ ⚠️ 公开仓库前务必清空 DEV_HOST / REGISTRY_PASS ║
|
|
# ║ 新增环境:在 env_profile 加一段 case,并把环境名加进 ENVS 数组。 ║
|
|
# ╚══════════════════════════════════════════════════════════════════════╝
|
|
ENVS=(lingpu yunyan along) # 可选测试环境(交互菜单按此顺序展示)
|
|
DEFAULT_ENV="lingpu" # 无参数且无 TTY(CI/管道)时的默认环境
|
|
|
|
env_profile() {
|
|
DEV_PORT=22 # 各环境通用默认
|
|
DEV_USER=root
|
|
case "$DEV_ENV" in
|
|
lingpu)
|
|
ENV_DESC="灵谱 · 香港阿里云 (lingpu-hk)"
|
|
registry_profile lingpu # 镜像仓库:灵谱
|
|
DEV_HOST="8.217.193.140" # ← 灵谱测试服务器 IP
|
|
DEV_KEY=/Users/liwei/Documents/灵谱/lingpu-hk.pem # 本机访问该服务器的 SSH 私钥
|
|
DEV_DIR="/home/work/starpivot/app" # 远端部署目录(其中 docker-compose.yml/.env/confs 由你自己维护)
|
|
;;
|
|
yunyan)
|
|
ENV_DESC="云雁测试环境 · 广州阿里云"
|
|
registry_profile yunyan # 镜像仓库:云雁(registry.voitrans.net,就在这台机上)
|
|
DEV_HOST="8.138.86.223" # ← 云雁测试服务器 IP(2026-07 由腾讯云 124.222.232.218 迁来)
|
|
DEV_KEY="/Users/liwei1dao/work/密钥/dev-guangzhou.pem" # 本机访问该服务器的 SSH 私钥(2026-08-18 由 ~/.ssh/aliyun_guangzhou.pem 迁到此处)
|
|
DEV_DIR="/home/work/starpivot/app"
|
|
;;
|
|
along)
|
|
ENV_DESC="阿龙测试环境 · 阿里云"
|
|
registry_profile along # 镜像仓库:就在这台机上(registry.ymaikj.com,账号 admin)
|
|
DEV_HOST="8.133.166.29" # ← 阿龙测试服务器 IP
|
|
DEV_KEY="${DEV_KEY:-/Users/liwei1dao/liwei/密钥/along/releae-guangzhou.pem}" # 本机访问该服务器的 SSH 私钥(2026-08-18 由 ~/.ssh/along.pem 迁到此处;文件名 releae 是原文件笔误)
|
|
DEV_DIR="/home/work/starpivot/app"
|
|
;;
|
|
*) red "✗ 未知环境: ${DEV_ENV}(应为 ${ENVS[*]// /|})"; exit 1 ;;
|
|
esac
|
|
}
|
|
# 取某环境的目标主机(供交互菜单展示):子 shell 里跑,不污染当前环境变量
|
|
env_host_of() { ( DEV_ENV="$1" env_profile >/dev/null 2>&1; printf '%s' "${DEV_HOST:-}" ); }
|
|
|
|
usage() {
|
|
cat >&2 <<EOF
|
|
用法: ./dev-deploy.sh [环境] (在 deploy/app/ 下运行)
|
|
环境 : ${ENVS[*]// /, }(省略则交互选择;无 TTY 时用默认 ${DEFAULT_ENV})
|
|
例:
|
|
./dev-deploy.sh lingpu # 发布到灵谱测试环境
|
|
./dev-deploy.sh yunyan # 发布到云雁测试环境
|
|
./dev-deploy.sh along # 发布到阿龙测试环境
|
|
EOF
|
|
}
|
|
|
|
# ── 参数解析:环境(缺省 → 交互选择)──
|
|
case "${1:-}" in -h|--help|help) usage; exit 0 ;; esac
|
|
DEV_ENV="${1:-}"
|
|
if [ -z "$DEV_ENV" ]; then
|
|
if [ -t 0 ]; then
|
|
blue "选择要发布的测试环境:"
|
|
i=1
|
|
for e in "${ENVS[@]}"; do
|
|
h="$(env_host_of "$e")"
|
|
printf " %d) %-8s → %s\n" "$i" "$e" "${h:-(未配置 IP,需先在脚本 env_profile 填)}" >&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
|
|
env_profile
|
|
green "✓ 目标环境: ${DEV_ENV}(${ENV_DESC})"
|
|
|
|
PLATFORM="${PLATFORM:-linux/amd64}"
|
|
TAG="dev-latest" # 开发版固定 tag,每次构建覆盖同一 dev-latest
|
|
FULL_IMAGE="$REGISTRY/$IMAGE_NAME:$TAG"
|
|
|
|
[ -f "$DOCKERFILE" ] || { red "✗ 缺少 $DOCKERFILE"; exit 1; }
|
|
: "${DEV_HOST:?请在 dev-deploy.sh 的 env_profile 中为环境 $DEV_ENV 填 DEV_HOST(服务器 IP)}"
|
|
|
|
# ── 远程 SSH ──
|
|
_ssh() { ssh -p "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new "$DEV_USER@$DEV_HOST" "$@"; }
|
|
_scp() { scp -P "$DEV_PORT" -i "${DEV_KEY/#\~/$HOME}" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new "$@"; }
|
|
_dest() { echo "$DEV_USER@$DEV_HOST:$1"; }
|
|
|
|
# 1) 构建并推送镜像
|
|
blue ">>> 构建镜像 $FULL_IMAGE (context=$BUILD_CONTEXT)"
|
|
echo "$REGISTRY_PASS" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
|
docker build --network=host --platform "$PLATFORM" -f "$DOCKERFILE" -t "$FULL_IMAGE" "$BUILD_CONTEXT"
|
|
docker push "$FULL_IMAGE"
|
|
|
|
# 2) 同步「模板 + compose」到服务器(只传模板,绝不覆盖你维护的真实 .env / confs/*.yaml)
|
|
blue ">>> 同步模板与 compose → $DEV_USER@$DEV_HOST:$DEV_DIR"
|
|
_ssh "mkdir -p '$DEV_DIR/confs' '$DEV_DIR/log'"
|
|
_scp "$SVC_DIR/docker-compose.yml" "$(_dest "$DEV_DIR/docker-compose.yml")"
|
|
_scp "$SVC_DIR"/confs/*.yaml.example "$(_dest "$DEV_DIR/confs/")"
|
|
_scp "$SVC_DIR/env/env.example" "$(_dest "$DEV_DIR/env.example")"
|
|
# ip2region 离线 IP 库(静态数据非凭据,home 的 sys.ipinfo 启动必需,缺了会 panic)
|
|
_scp "$REPO_ROOT"/apps/services/sys/ipinfo/ip2region_v*.xdb "$(_dest "$DEV_DIR/confs/")"
|
|
|
|
# 3) 远端拉取并重启(用你在服务器维护的真实 .env / confs/*.yaml)
|
|
# REGISTRY/TAG 通过 inline 环境变量传给 compose(优先级高于 --env-file),确保拉到本次镜像。
|
|
blue ">>> 远端拉取并重启"
|
|
_ssh "echo '$REGISTRY_PASS' | docker login '$REGISTRY' -u '$REGISTRY_USER' --password-stdin >/dev/null"
|
|
_ssh "docker network inspect 1panel-network >/dev/null 2>&1 || docker network create 1panel-network"
|
|
if ! _ssh "test -f '$DEV_DIR/.env'"; then
|
|
red "⚠ 服务器缺少真实 .env —— 模板已同步,请登录服务器按模板填好后重跑本脚本:"
|
|
echo " ssh -i $DEV_KEY $DEV_USER@$DEV_HOST"
|
|
echo " cd $DEV_DIR && cp env.example .env # 编辑 .env 填真实值,然后重跑 ./dev-deploy.sh"
|
|
exit 0
|
|
fi
|
|
_ssh "bash -lc 'for f in gateway api home mcp timer; do if [ ! -f \"$DEV_DIR/confs/\$f.yaml\" ] && [ -f \"$DEV_DIR/confs/\$f.yaml.example\" ]; then cp \"$DEV_DIR/confs/\$f.yaml.example\" \"$DEV_DIR/confs/\$f.yaml\"; fi; done'"
|
|
# compose 里的 service 名由 starpivot-app 改成通用的 app 后,服务器上按旧 service 名建的容器
|
|
# 既不属于新 service、又占着容器名 starpivot-app,直接 up 会报 "container name is already in use"。
|
|
# 它反正要被 --force-recreate 重建,这里先按 compose 标签认一下,不是本 service 的同名容器就删掉。
|
|
_ssh "old=\$(docker inspect -f '{{index .Config.Labels \"com.docker.compose.service\"}}' starpivot-app 2>/dev/null || true); if [ -n \"\$old\" ] && [ \"\$old\" != 'app' ]; then echo \"→ 移除旧 service '\$old' 留下的同名容器 starpivot-app\"; docker rm -f starpivot-app >/dev/null 2>&1 || true; fi"
|
|
# --remove-orphans:按旧 service 名建的容器在本项目里已成孤儿,一并清掉。
|
|
_ssh "cd '$DEV_DIR' && REGISTRY='$REGISTRY' IMAGE_NAME='$IMAGE_NAME' TAG='$TAG' docker compose --env-file .env pull && REGISTRY='$REGISTRY' IMAGE_NAME='$IMAGE_NAME' TAG='$TAG' docker compose --env-file .env up -d --force-recreate --remove-orphans && docker image prune -f"
|
|
|
|
green "✓ app dev[$DEV_ENV] 已发布: $FULL_IMAGE → $DEV_HOST:$DEV_DIR"
|
|
|