#!/usr/bin/env bash set -Eeuo pipefail set +x SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname -- "$SCRIPT_DIR")" cd "$PROJECT_ROOT" # Jenkins 页面只需要配置 ACR_REGISTRY 和 DEPLOY_TO_ACK。 # 其余默认值已与当前 ACK 资源对齐,必要时仍可通过同名环境变量覆盖。 ACR_REGISTRY="${ACR_REGISTRY:-}" ACR_NAMESPACE="${ACR_NAMESPACE:-one-feel}" API_REPOSITORY="${API_REPOSITORY:-wanderq-api}" ADMIN_REPOSITORY="${ADMIN_REPOSITORY:-wanderq-admin}" ACK_NAMESPACE="${ACK_NAMESPACE:-wanqu}" API_DEPLOYMENT="${API_DEPLOYMENT:-wanderq-api}" API_CONTAINER="${API_CONTAINER:-}" ADMIN_DEPLOYMENT="${ADMIN_DEPLOYMENT:-wanderq-admin}" ADMIN_CONTAINER="${ADMIN_CONTAINER:-}" ADMIN_API_BASE_URL="${ADMIN_API_BASE_URL:-}" DEPLOY_TO_ACK="${DEPLOY_TO_ACK:-false}" BUILD_NUMBER="${BUILD_NUMBER:-local}" DOCKER_BUILDKIT="${DOCKER_BUILDKIT:-1}" export DOCKER_BUILDKIT log() { printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" } fail() { printf '错误:%s\n' "$*" >&2 exit 1 } require_command() { command -v "$1" >/dev/null 2>&1 || fail "Jenkins Agent 缺少命令:$1" } [[ -n "$ACR_REGISTRY" ]] || fail "ACR_REGISTRY 不能为空" [[ "$ACR_REGISTRY" != *://* ]] || fail "ACR_REGISTRY 只填写域名,不要包含协议" [[ -n "$ACR_NAMESPACE" ]] || fail "ACR_NAMESPACE 不能为空" case "$DEPLOY_TO_ACK" in true|false) ;; *) fail "DEPLOY_TO_ACK 只能是 true 或 false" ;; esac require_command git require_command docker docker version >/dev/null if [[ "$DEPLOY_TO_ACK" == "true" ]]; then require_command kubectl [[ -n "${ACK_KUBECONFIG:-}" ]] || fail "未绑定 Jenkins kubeconfig 文件变量 ACK_KUBECONFIG" [[ -f "$ACK_KUBECONFIG" ]] || fail "ACK_KUBECONFIG 指向的文件不存在" fi GIT_COMMIT="$(git rev-parse --short=12 HEAD)" IMAGE_TAG="${IMAGE_TAG:-${GIT_COMMIT}-${BUILD_NUMBER}}" API_IMAGE="${ACR_REGISTRY}/${ACR_NAMESPACE}/${API_REPOSITORY}" ADMIN_IMAGE="${ACR_REGISTRY}/${ACR_NAMESPACE}/${ADMIN_REPOSITORY}" test_container="" cleanup() { if [[ -n "$test_container" ]]; then docker rm --force "$test_container" >/dev/null 2>&1 || true fi } trap cleanup EXIT log "构建 API 镜像:$API_IMAGE:$IMAGE_TAG" docker build \ --tag "$API_IMAGE:$IMAGE_TAG" \ WonderQ-Admin log "运行 API 测试" test_container="$(docker create \ --entrypoint python \ "$API_IMAGE:$IMAGE_TAG" \ -m pytest)" docker cp WonderQ-Admin/tests/. "$test_container:/app/tests" docker cp WonderQ-Admin/docker-compose.yml "$test_container:/app/docker-compose.yml" docker start --attach "$test_container" docker rm "$test_container" >/dev/null test_container="" log "构建 Admin UI 镜像:$ADMIN_IMAGE:$IMAGE_TAG" docker build \ --build-arg "VITE_API_BASE_URL=$ADMIN_API_BASE_URL" \ --tag "$ADMIN_IMAGE:$IMAGE_TAG" \ WonderQ-Admin-UI-Vue log "检查 Admin UI Nginx 配置" docker run --rm \ --entrypoint nginx \ "$ADMIN_IMAGE:$IMAGE_TAG" \ -t log "推送镜像" docker push "$API_IMAGE:$IMAGE_TAG" docker push "$ADMIN_IMAGE:$IMAGE_TAG" if [[ "$DEPLOY_TO_ACK" != "true" ]]; then log "镜像已推送,DEPLOY_TO_ACK=false,本次不更新 ACK" exit 0 fi KUBECTL=( kubectl --kubeconfig "$ACK_KUBECONFIG" --namespace "$ACK_NAMESPACE" ) log "检查 ACK Deployment" "${KUBECTL[@]}" get deployment "$API_DEPLOYMENT" >/dev/null "${KUBECTL[@]}" get deployment "$ADMIN_DEPLOYMENT" >/dev/null # 当前两个 Deployment 均为单容器;自动读取容器名,避免把 Pod 名称或历史拼写写进 Jenkins 参数。 if [[ -z "$API_CONTAINER" ]]; then API_CONTAINER="$("${KUBECTL[@]}" get deployment "$API_DEPLOYMENT" \ -o jsonpath='{.spec.template.spec.containers[0].name}')" fi if [[ -z "$ADMIN_CONTAINER" ]]; then ADMIN_CONTAINER="$("${KUBECTL[@]}" get deployment "$ADMIN_DEPLOYMENT" \ -o jsonpath='{.spec.template.spec.containers[0].name}')" fi [[ -n "$API_CONTAINER" ]] || fail "无法读取 API Deployment 的容器名称" [[ -n "$ADMIN_CONTAINER" ]] || fail "无法读取 Admin UI Deployment 的容器名称" log "更新 API Deployment:$API_DEPLOYMENT/$API_CONTAINER" "${KUBECTL[@]}" set image \ "deployment/$API_DEPLOYMENT" \ "$API_CONTAINER=$API_IMAGE:$IMAGE_TAG" if ! "${KUBECTL[@]}" rollout status \ "deployment/$API_DEPLOYMENT" \ --timeout=300s; then "${KUBECTL[@]}" rollout undo "deployment/$API_DEPLOYMENT" || true fail "API 发布失败,已尝试回滚 Deployment;数据库迁移不会自动回滚" fi log "更新 Admin UI Deployment:$ADMIN_DEPLOYMENT/$ADMIN_CONTAINER" "${KUBECTL[@]}" set image \ "deployment/$ADMIN_DEPLOYMENT" \ "$ADMIN_CONTAINER=$ADMIN_IMAGE:$IMAGE_TAG" if ! "${KUBECTL[@]}" rollout status \ "deployment/$ADMIN_DEPLOYMENT" \ --timeout=300s; then "${KUBECTL[@]}" rollout undo "deployment/$ADMIN_DEPLOYMENT" || true "${KUBECTL[@]}" rollout undo "deployment/$API_DEPLOYMENT" || true fail "Admin UI 发布失败,已尝试回滚两个 Deployment" fi log "ACK 发布完成,镜像标签:$IMAGE_TAG"