Files
awoooi/.github/workflows/ci.yaml
OG T b8f9cd315c fix(ci): replace jq with python3 for JSON parsing in Ollama test
The self-hosted runner doesn't have jq installed.
Use Python's json module as a portable alternative.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-25 12:07:23 +08:00

324 lines
9.6 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.
# =============================================================================
# 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
# ==================== Ollama Model Test (Phase 12.3 #67) ====================
# 🤖 自動化模型回歸測試 - 確保 OpenClaw 提案品質
ollama-test:
name: Ollama Model Test
runs-on: [self-hosted, harbor, k8s]
needs: api-lint
timeout-minutes: 5
continue-on-error: true # 不阻塞主 Pipeline
steps:
- uses: actions/checkout@v4
- name: Test Ollama Connectivity
run: |
echo "🔗 測試 Ollama 連線..."
if curl -s --connect-timeout 5 http://192.168.0.188:11434/api/tags > /dev/null; then
echo "✅ Ollama 可達"
# 使用 Python 替代 jq (Runner 未安裝 jq)
curl -s http://192.168.0.188:11434/api/tags | python3 -c "import sys,json; [print(m['name']) for m in json.load(sys.stdin).get('models',[])]"
else
echo "⚠️ Ollama 無法連線 (192.168.0.188:11434)"
exit 0 # 不失敗,只警告
fi
- name: Model Smoke Test
run: |
echo "🧪 模型冒煙測試..."
# 使用 Python 替代 jq
RESPONSE=$(curl -s --max-time 60 http://192.168.0.188:11434/api/generate -d '{
"model": "qwen2.5:7b-instruct",
"prompt": "你是 AIOps 助手。回答1+1=?",
"stream": false
}' | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('response','ERROR'))" 2>/dev/null || echo "ERROR")
if [ "$RESPONSE" != "ERROR" ] && [ -n "$RESPONSE" ]; then
echo "✅ 模型回應正常"
echo "回應: $RESPONSE"
else
echo "⚠️ 模型回應異常"
fi
- name: Action Parsing Test
working-directory: apps/api
env:
PYTHONPATH: ${{ github.workspace }}/apps/api
run: |
echo "🔍 Action Parsing 回歸測試..."
uv sync
uv run pytest tests/test_action_parsing.py -v --tb=short || echo "⚠️ 部分測試失敗"
- name: Prompt Validation Test
working-directory: apps/api
env:
PYTHONPATH: ${{ github.workspace }}/apps/api
OLLAMA_URL: http://192.168.0.188:11434
run: |
echo "📝 Prompt 品質驗證..."
uv run pytest tests/test_prompt_validation.py::test_prompt_quality_report -v --tb=short || echo "⚠️ Prompt 驗證略過"
# ==================== 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 ====================
# 🚀 優化 (2026-03-24): 只在 PR 時執行main push 跳過 (CD 會構建)
docker-build:
name: Docker Verify
runs-on: [self-hosted, harbor, k8s]
needs: [test, api-test, build]
if: github.event_name == 'pull_request'
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