224 lines
9.5 KiB
YAML
224 lines
9.5 KiB
YAML
# =============================================================================
|
||
# TIANAI CAPTCHA PLATFORM — 测试环境自动部署
|
||
# 触发:push 到 master(或手动 workflow_dispatch)
|
||
# 流程:checkout → Maven 容器构建 jar → docker build → 推 Registry(:test + :sha-x)
|
||
# → SSH 到测试机(156.233.226.86) 重建容器 → 健康检查
|
||
# 架构参考:perler-lab deploy-test.yml
|
||
# =============================================================================
|
||
name: deploy-test
|
||
|
||
on:
|
||
push:
|
||
branches: [master]
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: deploy-test-master
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
REGISTRY: 156.233.226.46:5000
|
||
IMAGE: 156.233.226.46:5000/tianai-captcha-platform
|
||
|
||
jobs:
|
||
build-and-deploy-test:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- name: Checkout master
|
||
uses: actions/checkout@v4
|
||
with:
|
||
ref: master
|
||
|
||
- name: Setup SSH key (from secrets)
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
printf '%s\n' "${{ secrets.CAPTCHA_DEPLOY_KEY }}" > ~/.ssh/id_ed25519_deploy
|
||
chmod 600 ~/.ssh/id_ed25519_deploy
|
||
ssh-keyscan -H ${{ secrets.CAPTCHA_TEST_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
ssh-keyscan -H ${{ secrets.CAPTCHA_PROD_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
echo "SSH 密钥就绪"
|
||
|
||
- name: Build jar (Maven + JDK21 in docker, 带依赖缓存卷)
|
||
# 注意: job 容器内的路径对宿主机 daemon 不可见, 不能用 -v "$PWD" 挂载;
|
||
# 用 docker cp 流式传文件(与 build context 同机制)
|
||
run: |
|
||
set -e
|
||
docker volume create tianai-maven-cache >/dev/null 2>&1 || true
|
||
docker rm -f mvnbuild 2>/dev/null || true
|
||
docker run -d --name mvnbuild -v tianai-maven-cache:/root/.m2 -w /ws \
|
||
maven:3.9-eclipse-temurin-21 sleep infinity
|
||
docker cp "$GITHUB_WORKSPACE/." mvnbuild:/ws/
|
||
docker exec mvnbuild mvn -q package -DskipTests
|
||
docker cp mvnbuild:/ws/tianai-captcha-platform/target/tianai-captcha-platform-2.0.0-SNAPSHOT.jar \
|
||
"$GITHUB_WORKSPACE/tianai-captcha-platform/tianai-captcha-platform-2.0.0-SNAPSHOT.jar"
|
||
docker rm -f mvnbuild
|
||
ls -la tianai-captcha-platform/*.jar
|
||
|
||
- name: Build frontend SDK (webpack)
|
||
run: |
|
||
set -e
|
||
docker rm -f tacbuild 2>/dev/null || true
|
||
docker run -d --name tacbuild -w /ws node:20-alpine sleep infinity
|
||
docker cp "$GITHUB_WORKSPACE/tianai-captcha-web-sdk/." tacbuild:/ws/
|
||
docker exec tacbuild sh -c "npm install --prefer-offline && npm run buildprod"
|
||
docker cp tacbuild:/ws/dist "$GITHUB_WORKSPACE/tac-dist"
|
||
docker rm -f tacbuild
|
||
echo "=== 构建产物 ==="
|
||
ls -la "$GITHUB_WORKSPACE/tac-dist/tac/js/" "$GITHUB_WORKSPACE/tac-dist/tac/css/" 2>/dev/null
|
||
|
||
- name: Build admin dashboard (Vue3 + Vite)
|
||
run: |
|
||
set -e
|
||
docker rm -f adminbuild 2>/dev/null || true
|
||
docker run -d --name adminbuild -w /ws node:20-alpine sleep infinity
|
||
docker cp "$GITHUB_WORKSPACE/tianai-captcha-platform-ui/." adminbuild:/ws/
|
||
docker exec adminbuild sh -c "npm install --prefer-offline && npm run build"
|
||
docker cp adminbuild:/ws/dist "$GITHUB_WORKSPACE/admin-dist"
|
||
docker rm -f adminbuild
|
||
echo "=== 管理后台构建产物 ==="
|
||
ls -la "$GITHUB_WORKSPACE/admin-dist/" 2>/dev/null
|
||
|
||
- name: Build & push image
|
||
run: |
|
||
set -e
|
||
SHA=$(echo "$GITHUB_SHA" | cut -c1-8)
|
||
docker build -t $IMAGE:test -t $IMAGE:sha-$SHA tianai-captcha-platform
|
||
docker push $IMAGE:test
|
||
docker push $IMAGE:sha-$SHA
|
||
echo "已推送 $IMAGE:test 和 $IMAGE:sha-$SHA"
|
||
|
||
- name: Deploy to test env
|
||
run: |
|
||
set -e
|
||
SSH_CMD="ssh -i $HOME/.ssh/id_ed25519_deploy root@${{ secrets.CAPTCHA_TEST_HOST }}"
|
||
$SSH_CMD '
|
||
set -e
|
||
echo "===== 拉取镜像 ====="
|
||
docker pull 156.233.226.46:5000/tianai-captcha-platform:test
|
||
echo "===== 重建后端容器 ====="
|
||
docker rm -f tianai-captcha 2>/dev/null || true
|
||
docker run -d --name tianai-captcha \
|
||
--network mw \
|
||
--restart unless-stopped \
|
||
-p 127.0.0.1:18200:18200 \
|
||
-v /opt/tianai/config:/app/config \
|
||
156.233.226.46:5000/tianai-captcha-platform:test
|
||
echo "===== 部署前端 SDK ====="
|
||
mkdir -p /opt/tianai/web/tac
|
||
'
|
||
# 上传前端 SDK 构建产物
|
||
scp -i $HOME/.ssh/id_ed25519_deploy -r "$GITHUB_WORKSPACE/tac-dist/tac"/* \
|
||
root@${{ secrets.CAPTCHA_TEST_HOST }}:/opt/tianai/web/tac/
|
||
# 上传管理后台构建产物
|
||
mkdir -p /tmp/admin-dist
|
||
cp -r "$GITHUB_WORKSPACE/admin-dist/"* /tmp/admin-dist/
|
||
scp -i $HOME/.ssh/id_ed25519_deploy -r /tmp/admin-dist/* \
|
||
root@${{ secrets.CAPTCHA_TEST_HOST }}:/opt/tianai/web/admin/
|
||
# 上传 nginx 配置
|
||
cat > /tmp/captcha-nginx.conf <<'NGINX_EOF'
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
gzip on;
|
||
gzip_static on;
|
||
gzip_types text/plain text/css application/json application/javascript text/xml image/svg+xml;
|
||
gzip_min_length 256;
|
||
|
||
# 管理后台入口文件不缓存
|
||
location = /admin/index.html {
|
||
alias /usr/share/nginx/html/admin/index.html;
|
||
add_header Cache-Control "no-cache, must-revalidate";
|
||
}
|
||
|
||
# 管理后台 (Vue3 SPA)
|
||
location /admin/ {
|
||
alias /usr/share/nginx/html/admin/;
|
||
try_files $uri $uri/ /admin/index.html;
|
||
}
|
||
|
||
# 后端 SDK 静态文件
|
||
location /tac/ {
|
||
root /usr/share/nginx/html;
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
|
||
# 后端 API 反代 (前端 SDK 调用)
|
||
location ^~ /tac/api/ {
|
||
proxy_pass http://tianai-captcha:18200/api/;
|
||
proxy_set_header Host $http_host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
|
||
# 管理后台 API 反代
|
||
location ^~ /admin/api/ {
|
||
proxy_pass http://tianai-captcha:18200/api/;
|
||
proxy_set_header Host $http_host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}
|
||
NGINX_EOF
|
||
scp -i $HOME/.ssh/id_ed25519_deploy /tmp/captcha-nginx.conf \
|
||
root@${{ secrets.CAPTCHA_TEST_HOST }}:/opt/tianai/web/nginx.conf
|
||
$SSH_CMD '
|
||
echo "===== 重建 nginx 容器 ====="
|
||
docker rm -f tianai-web 2>/dev/null || true
|
||
docker run -d --name tianai-web \
|
||
--network mw \
|
||
--restart unless-stopped \
|
||
-p 0.0.0.0:8080:80 \
|
||
-v /opt/tianai/web:/usr/share/nginx/html:ro \
|
||
-v /opt/tianai/web/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
|
||
nginx:alpine
|
||
sleep 3
|
||
docker ps --filter name=tianai-web --format "{{.Names}} {{.Status}}"
|
||
'
|
||
|
||
- name: Health check (generate API + 前端页面)
|
||
env:
|
||
TEST_SITE_KEY: 6a062b70-c0b3-428e-8f1b-cca402e67ddd
|
||
run: |
|
||
set -e
|
||
SSH_CMD="ssh -i $HOME/.ssh/id_ed25519_deploy root@${{ secrets.CAPTCHA_TEST_HOST }}"
|
||
OK=0
|
||
CODE=000
|
||
for i in $(seq 1 24); do
|
||
CODE=$($SSH_CMD "curl -s -X POST 'http://127.0.0.1:18200/api/challenge/generate' -H 'X-Site-Key: $TEST_SITE_KEY'" | python3 -c "import json,sys; print(json.load(sys.stdin).get('code'))" 2>/dev/null || echo 000)
|
||
if [ "$CODE" = "200" ]; then OK=1; break; fi
|
||
sleep 5
|
||
done
|
||
if [ "$OK" != "1" ]; then
|
||
echo "后端 API 健康检查失败 (code=$CODE)"
|
||
$SSH_CMD 'docker logs tianai-captcha --tail 30' || true
|
||
exit 1
|
||
fi
|
||
echo "后端 API 健康检查 OK (code=$CODE)"
|
||
# 检查前端 SDK
|
||
HTTP_CODE=$($SSH_CMD "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/tac/js/tac.min.js" 2>/dev/null || echo 000)
|
||
if [ "$HTTP_CODE" = "200" ]; then
|
||
echo "前端 SDK 部署 OK (http_code=$HTTP_CODE)"
|
||
else
|
||
echo "前端 SDK 部署异常 (http_code=$HTTP_CODE)"
|
||
exit 1
|
||
fi
|
||
# 检查管理后台
|
||
ADMIN_CODE=$($SSH_CMD "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/admin/" 2>/dev/null || echo 000)
|
||
if [ "$ADMIN_CODE" = "200" ]; then
|
||
echo "管理后台部署 OK (http_code=$ADMIN_CODE)"
|
||
else
|
||
echo "管理后台部署异常 (http_code=$ADMIN_CODE)"
|
||
exit 1
|
||
fi
|
||
|
||
- name: Deployment summary
|
||
if: always()
|
||
run: |
|
||
echo "commit: $GITHUB_SHA"
|
||
echo "后端 API: http://<test-ip>:8080/tac/api/... (proxy → tianai-captcha:18200)"
|
||
echo "前端 SDK: http://<test-ip>:8080/tac/js/tac.min.js"
|
||
echo "管理后台: http://<test-ip>:8080/admin/"
|
||
echo "晋升生产: Gitea → 仓库 → Actions → promote-prod → 输入 yes"
|