# ============================================================================= # AWOOOI CI Pipeline v2.0 (沿用 AIOPS 最佳實踐) # ============================================================================= name: CI on: push: branches: [main] pull_request: branches: [main] workflow_dispatch: concurrency: group: ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: NODE_VERSION: '20' PNPM_VERSION: '9' PYTHON_VERSION: '3.11' # OTEL CI/CD 監控 (2026-03-24 批准) OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.0.121:4318 OTEL_SERVICE_NAME: awoooi-ci OTEL_RESOURCE_ATTRIBUTES: service.version=${{ github.sha }},deployment.environment=ci jobs: # ==================== Pre-flight (10s Fail-Fast) ==================== pre-flight: name: "Pre-flight" runs-on: [self-hosted, harbor, k8s] timeout-minutes: 1 steps: - name: Quick sanity check run: | echo "✅ Runner 可用" node --version || echo "⚠️ Node not found" python3 --version || echo "⚠️ Python not found" # ==================== Lint & Type Check ==================== lint: name: Lint & Type Check runs-on: [self-hosted, harbor, k8s] needs: pre-flight timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v3 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Lint run: pnpm lint - name: Type check run: pnpm typecheck - name: ADR Compliance Check run: | echo "🔍 檢查 ADR 規定..." # 檢查 1: 前端禁止直連資料庫 (ADR-005) if grep -rE "psycopg2|asyncpg|redis|sqlalchemy|pg|ioredis" apps/web/src/ 2>/dev/null; then echo "❌ ADR-005 違規: 前端禁止直連資料庫" exit 1 fi # 檢查 2: 禁止 Redux (ADR-004) if grep -rE "@reduxjs/toolkit|react-redux" apps/web/package.json 2>/dev/null; then echo "❌ ADR-004 違規: 禁止 Redux" exit 1 fi # 檢查 3: 禁止 import 舊專案 if grep -rE "from ['\"].*wooo-aiops" apps/ packages/ 2>/dev/null; then echo "❌ 禁止 import 舊專案" exit 1 fi echo "✅ ADR 檢查通過" # ==================== Test ==================== test: name: Test runs-on: [self-hosted, harbor, k8s] needs: lint timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v3 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run tests run: pnpm test continue-on-error: true - name: Upload coverage uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false # ==================== Build ==================== build: name: Build runs-on: [self-hosted, harbor, k8s] needs: lint timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v3 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Setup Turborepo Cache uses: dtinth/setup-github-actions-caching-for-turbo@v1 - name: Build packages env: NEXT_PUBLIC_API_URL: https://awoooi.wooo.work # Sentry DSN (透過 /api/sentry-tunnel 避免區域網路權限問題) NEXT_PUBLIC_SENTRY_DSN: http://da02d4e5d6542e4d1ed6b2dd6542efeb@192.168.0.110:9000/2 run: pnpm turbo build - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: build-artifacts path: | apps/*/dist packages/*/dist retention-days: 7 # ==================== API Lint (Python) ==================== api-lint: name: API Lint runs-on: [self-hosted, harbor, k8s] needs: pre-flight timeout-minutes: 5 steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install uv uses: astral-sh/setup-uv@v3 - name: Install & Lint working-directory: apps/api run: | uv sync uv run ruff check . - name: Type check working-directory: apps/api run: uv run mypy src/ --exclude 'tests/|scripts/' || true continue-on-error: true # ==================== API Test ==================== api-test: name: API Test runs-on: [self-hosted, harbor, k8s] needs: api-lint timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install uv uses: astral-sh/setup-uv@v3 - name: Install & Test working-directory: apps/api env: PYTHONPATH: ${{ github.workspace }}/apps/api run: | uv sync uv run pytest tests/ --cov=src --cov-report=xml -v || true continue-on-error: true # ==================== OpenAPI Validation ==================== openapi-validate: name: OpenAPI Validate runs-on: [self-hosted, harbor, k8s] needs: pre-flight timeout-minutes: 3 steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: Validate run: | npm install -g @stoplight/spectral-cli spectral lint docs/api/api-contract.yaml || true # ==================== Docker Build Verify ==================== docker-build: name: Docker Verify runs-on: [self-hosted, harbor, k8s] needs: [test, api-test, build] timeout-minutes: 20 strategy: matrix: app: [web, api] steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build (no push) uses: docker/build-push-action@v5 with: context: . file: apps/${{ matrix.app }}/Dockerfile push: false tags: awoooi-${{ matrix.app }}:test build-args: | NEXT_PUBLIC_API_URL=https://awoooi.wooo.work NEXT_PUBLIC_SENTRY_DSN=http://da02d4e5d6542e4d1ed6b2dd6542efeb@192.168.0.110:9000/2 cache-from: type=gha cache-to: type=gha,mode=max