From f33812cf7b96c595627ca9e312c9b6bab95b6009 Mon Sep 17 00:00:00 2001 From: wangdl Date: Thu, 18 Jun 2026 13:57:46 +0800 Subject: [PATCH] ci: add GitHub Actions CI/CD pipeline (API-AI-078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 5-stage pipeline: lint → test → build → docker-build → deploy - lint: ESLint with npx prisma generate - test: Jest with passWithNoTests, artifact upload - build: NestJS build + dist verification - docker-build: multi-stage Docker build for api + worker - deploy: main-only, docker push + SSH deploy with prisma migrate Co-Authored-By: Claude Opus 4.7 --- .github/workflows/ci.yml | 151 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e327166 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,151 @@ +name: CI/CD Pipeline + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +env: + NODE_VERSION: '22' + +jobs: + # ═══════════════════════════════════════════════════════════════ + # 1. Lint + # ═══════════════════════════════════════════════════════════════ + + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - run: npm ci + - run: npx prisma generate + env: + DATABASE_URL: mysql://placeholder:placeholder@localhost:3306/placeholder + - run: npm run lint + + # ═══════════════════════════════════════════════════════════════ + # 2. Test + # ═══════════════════════════════════════════════════════════════ + + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: lint + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - run: npm ci + - run: npx prisma generate + env: + DATABASE_URL: mysql://placeholder:placeholder@localhost:3306/placeholder + - run: npm test -- --passWithNoTests + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-results + path: coverage/ + retention-days: 7 + + # ═══════════════════════════════════════════════════════════════ + # 3. Build + # ═══════════════════════════════════════════════════════════════ + + build: + name: Build + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: test + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + - run: npm ci + - run: npx prisma generate + env: + DATABASE_URL: mysql://placeholder:placeholder@localhost:3306/placeholder + - run: npm run build + - name: Verify dist output + run: test -f dist/main.js && echo "Build OK" + + # ═══════════════════════════════════════════════════════════════ + # 4. Docker Build (integration test) + # ═══════════════════════════════════════════════════════════════ + + docker-build: + name: Docker Build + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: build + steps: + - uses: actions/checkout@v4 + - name: Build API image + run: docker build -t zhixi-api:${{ github.sha }} -f Dockerfile . + - name: Build Worker image + run: docker build -t zhixi-worker:${{ github.sha }} -f Dockerfile.worker . + + # ═══════════════════════════════════════════════════════════════ + # 5. Deploy (main branch only) + # ═══════════════════════════════════════════════════════════════ + + deploy: + name: Deploy + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: docker-build + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + environment: production + steps: + - uses: actions/checkout@v4 + + - name: Docker login + uses: docker/login-action@v3 + with: + registry: ${{ vars.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push API image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile + push: true + tags: | + ${{ vars.DOCKER_REGISTRY }}/zhixi-api:latest + ${{ vars.DOCKER_REGISTRY }}/zhixi-api:${{ github.sha }} + + - name: Build and push Worker image + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.worker + push: true + tags: | + ${{ vars.DOCKER_REGISTRY }}/zhixi-worker:latest + ${{ vars.DOCKER_REGISTRY }}/zhixi-worker:${{ github.sha }} + + - name: Deploy via SSH + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + script: | + cd /opt/zhixi/api-server + docker compose pull + docker compose up -d --remove-orphans + docker compose exec -T api npx prisma migrate deploy