Files
Edu/.github/workflows/deploy.yml
SpecialX 3b88e9cca5 ci(infra): 完整 CI/CD 流水线配置
- ci-ts.yml: lint/typecheck/test/build + arch-scan + docker-build(classes + teacher-portal)

- ci-go.yml: vet/build/test + docker-build(api-gateway)

- ci-proto.yml: buf lint + buf breaking(本地 .git 比较)

- docker.yml: main/tag 触发,构建推送 3 服务镜像到 Gitea Registry

- deploy.yml: workflow_dispatch + workflow_run 触发,Runner 直接 docker compose 部署,含健康检查与回滚

- 所有 workflow runs-on: ubuntu-latest 匹配 actrunner 标签
2026-07-08 15:13:53 +08:00

141 lines
4.7 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Deploy
# 部署到服务器
# 触发条件main 分支 docker.yml 完成后 自动,或手动 workflow_dispatch
# 部署方式Runner 直接执行 docker composeRunner 跑在服务器上)
#
# 前置条件(一次性配置):
# 1. 服务器上已安装 Gitea Actions Runner标签为 'ubuntu-latest'
# 2. 服务器上已存在 /opt/edu/docker-compose.deploy.yml由 SRE AI 首次部署)
# 3. 服务器上已存在 /opt/edu/.env含生产 JWT_SECRET 等)
# 4. 服务器 Docker 已登录 Gitea Container Registry
# docker login git.eazygame.cn -u <user> -p <token>
# 5. MySQL + Redis 已在服务器 Docker 中运行3306/6379
on:
workflow_dispatch:
inputs:
image_tag:
description: '镜像 tag默认 latest'
required: false
default: 'latest'
rollback:
description: '回滚到上一版本(跳过 pull'
type: boolean
required: false
default: false
workflow_run:
workflows: ['Docker Build & Push']
types: [completed]
branches: [main]
permissions:
contents: read
env:
DEPLOY_DIR: /opt/edu
REGISTRY: git.eazygame.cn
IMAGE_TAG: ${{ github.event.inputs.image_tag || 'latest' }}
jobs:
deploy:
# 仅在 docker.yml 成功后触发,或手动触发
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
environment:
name: production
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup deploy dir
run: |
sudo mkdir -p ${{ env.DEPLOY_DIR }}
sudo chown -R $USER:$USER ${{ env.DEPLOY_DIR }}
- name: Sync compose file
run: |
cp infra/docker-compose.deploy.yml ${{ env.DEPLOY_DIR }}/docker-compose.yml
# 确保 .env 存在(首次部署需人工创建)
if [ ! -f ${{ env.DEPLOY_DIR }}/.env ]; then
echo "::error::部署目录缺少 .env 文件,请参考 infra/deploy.env.example 创建"
exit 1
fi
- name: Login to Gitea Container Registry
run: |
# 使用预存的 ~/.docker/config.json首次部署时人工 docker login
# 或使用 secrets 注入
if [ -n "${{ secrets.GITEA_REGISTRY_TOKEN }}" ]; then
echo "${{ secrets.GITEA_REGISTRY_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.GITEA_REGISTRY_USER }} --password-stdin
fi
- name: Pull images
if: ${{ github.event.inputs.rollback != 'true' }}
run: |
cd ${{ env.DEPLOY_DIR }}
export IMAGE_TAG=${{ env.IMAGE_TAG }}
docker compose pull
continue-on-error: true # 首次部署可能无旧镜像
- name: Deploy services
run: |
cd ${{ env.DEPLOY_DIR }}
export IMAGE_TAG=${{ env.IMAGE_TAG }}
docker compose up -d --remove-orphans
- name: Wait for health
run: |
echo "等待服务健康..."
sleep 10
for i in 1 2 3 4 5 6 7 8 9 10; do
FAIL=0
echo "[尝试 $i] 检查 api-gateway..."
if ! curl -sf http://localhost:8080/healthz > /dev/null; then
echo " api-gateway 未就绪"
FAIL=1
fi
echo "[尝试 $i] 检查 classes..."
if ! curl -sf http://localhost:3001/healthz > /dev/null; then
echo " classes 未就绪"
FAIL=1
fi
echo "[尝试 $i] 检查 teacher-portal..."
if ! curl -sf http://localhost:3000/ > /dev/null; then
echo " teacher-portal 未就绪"
FAIL=1
fi
if [ $FAIL -eq 0 ]; then
echo "✅ 所有服务健康"
exit 0
fi
sleep 6
done
echo "::error::服务健康检查失败"
echo "=== 容器状态 ==="
cd ${{ env.DEPLOY_DIR }} && docker compose ps
echo "=== api-gateway 日志 ==="
docker compose logs --tail=50 api-gateway
echo "=== classes 日志 ==="
docker compose logs --tail=50 classes
exit 1
- name: Summary
if: always()
run: |
echo "### 部署结果" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- 镜像 tag\`${{ env.IMAGE_TAG }}\`" >> $GITHUB_STEP_SUMMARY
echo "- 部署目录:\`${{ env.DEPLOY_DIR }}\`" >> $GITHUB_STEP_SUMMARY
echo "- 回滚模式:${{ github.event.inputs.rollback || 'false' }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
cd ${{ env.DEPLOY_DIR }} && docker compose ps >> $GITHUB_STEP_SUMMARY 2>&1 || true