duoqi-api/.gitea/workflows/deploy.yml
Wang Zhuoxuan e893755340 feat: 添加 CI/CD 部署方案(Gitea + Docker + 双分支工作流)
- Dockerfile: 多阶段构建,oven/bun 基础镜像,非 root 用户
- docker-compose.yml: 本地开发环境(API + MySQL + Drizzle Studio)
- docker-compose.prod.yml: 服务器部署(prod + test,Docker profiles)
- .gitea/workflows/deploy.yml: 双分支 CI/CD(develop→测试, main→生产)
- docs/ci-deployment-guide.md: 完整部署指南(Alibaba Cloud Linux 3)
- scripts/deploy.sh: 手动部署运维脚本
- .env.prod.example: 生产环境变量模板
- .dockerignore: 排除非构建文件
- .gitignore: 排除 .claude/ 目录
2026-04-16 12:44:14 +08:00

199 lines
5.3 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.

# 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: 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: 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: 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: 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