# Gitea Actions CI/CD 配置 # Duoqi API - 双分支工作流(develop → main) # # 工作流: # develop push → quality → test → build → 自动部署测试环境 # main push → quality → test → build → 手动确认部署生产环境 name: CI/CD Pipeline on: push: branches: [main, develop] env: DEPLOY_DIR: /opt/duoqi-api jobs: # ==================== 代码质量检查 ==================== quality: name: Code Quality runs-on: ubuntu-latest steps: - name: Install git run: apt-get update && apt-get install -y git - name: Checkout code uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install --frozen-lockfile - name: Run ESLint run: bun run lint - name: Type check run: bun run typecheck # ==================== 运行测试 ==================== test: name: Unit Tests runs-on: ubuntu-latest needs: quality steps: - name: Install git run: apt-get update && apt-get install -y git - name: Checkout code uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install --frozen-lockfile - name: Run tests with coverage run: bun run test:coverage - name: Upload coverage uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage/ # ==================== 构建测试镜像 ==================== build-test: name: Build Test Image runs-on: ubuntu-latest needs: [quality, test] if: github.ref == 'refs/heads/develop' steps: - name: Install git run: apt-get update && apt-get install -y git - name: Checkout code uses: actions/checkout@v4 - name: Build test image run: | docker build --build-arg NODE_ENV=test -t duoqi-api:test . mkdir -p /tmp/images docker save duoqi-api:test -o /tmp/images/duoqi-api-test.tar - name: Upload image artifact uses: actions/upload-artifact@v4 with: name: docker-images-test path: /tmp/images/ retention-days: 1 # ==================== 构建生产镜像 ==================== build-prod: name: Build Production Image runs-on: ubuntu-latest needs: [quality, test] if: github.ref == 'refs/heads/main' steps: - name: Install git run: apt-get update && apt-get install -y git - name: Checkout code uses: actions/checkout@v4 - name: Build production image run: | docker build -t duoqi-api:prod . mkdir -p /tmp/images docker save duoqi-api:prod -o /tmp/images/duoqi-api-prod.tar - name: Upload image artifact uses: actions/upload-artifact@v4 with: name: docker-images-prod path: /tmp/images/ retention-days: 1 # ==================== 部署到测试环境(develop push 自动触发) ==================== deploy-test: name: Deploy to Test runs-on: ubuntu-latest needs: build-test if: github.ref == 'refs/heads/develop' environment: name: test url: http://test-api.duoqi.me steps: - name: Download image artifact uses: actions/download-artifact@v4 with: name: docker-images-test path: /tmp/images/ - name: Load Docker image run: docker load -i /tmp/images/duoqi-api-test.tar - name: Deploy test environment run: | cd ${{ env.DEPLOY_DIR }} docker compose --profile test up -d --no-build api-test - name: Health check run: | sleep 10 for i in {1..5}; do if curl -f http://localhost:3001/health; then echo "Test environment is healthy!" exit 0 fi echo "Health check attempt $i failed, retrying..." sleep 5 done echo "Test environment health check failed" exit 1 # ==================== 部署到生产环境(main push,手动确认) ==================== deploy-prod: name: Deploy to Production runs-on: ubuntu-latest needs: build-prod if: github.ref == 'refs/heads/main' environment: name: production url: https://api.duoqi.me steps: - name: Download image artifact uses: actions/download-artifact@v4 with: name: docker-images-prod path: /tmp/images/ - name: Load Docker image run: docker load -i /tmp/images/duoqi-api-prod.tar - name: Deploy production run: | cd ${{ env.DEPLOY_DIR }} # 备份当前镜像(用于回滚) docker tag duoqi-api:prod duoqi-api:rollback 2>/dev/null || true # 滚动更新 docker compose up -d --no-build api-prod - name: Health check run: | sleep 15 for i in {1..5}; do if curl -f http://localhost:3000/health; then echo "Production deployment successful!" exit 0 fi echo "Health check attempt $i failed, retrying..." sleep 5 done # 健康检查失败,自动回滚 echo "Health check failed! Rolling back..." cd ${{ env.DEPLOY_DIR }} docker tag duoqi-api:rollback duoqi-api:prod docker compose up -d --no-build api-prod exit 1 - name: Cleanup if: always() run: | docker image prune -f rm -rf /tmp/images