# ============================================================================= # 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; # 后端 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; } # 管理后台 (Vue3 SPA) location /admin/ { alias /usr/share/nginx/html/admin/; try_files $uri $uri/ /admin/index.html; } # 管理后台 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://:8080/tac/api/... (proxy → tianai-captcha:18200)" echo "前端 SDK: http://:8080/tac/js/tac.min.js" echo "管理后台: http://:8080/admin/" echo "晋升生产: Gitea → 仓库 → Actions → promote-prod → 输入 yes"