158 lines
4.9 KiB
Bash
Executable File
158 lines
4.9 KiB
Bash
Executable File
#!/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"
|