chore: 测试 GitHub 推送权限(OAuth App 需组织授权) #443
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 0: Change detection (diff-based incremental) | |
| # ───────────────────────────────────────────────────────────────────────── | |
| detect-changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| run_frontend: ${{ steps.diff.outputs.run_frontend }} | |
| run_api: ${{ steps.diff.outputs.run_api }} | |
| run_agent_sdk: ${{ steps.diff.outputs.run_agent_sdk }} | |
| run_docker: ${{ steps.diff.outputs.run_docker }} | |
| run_worldweave: ${{ steps.diff.outputs.run_worldweave }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Diff and set outputs | |
| id: diff | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "run_frontend=true" >> "$GITHUB_OUTPUT" | |
| echo "run_api=true" >> "$GITHUB_OUTPUT" | |
| echo "run_agent_sdk=true" >> "$GITHUB_OUTPUT" | |
| echo "run_docker=true" >> "$GITHUB_OUTPUT" | |
| echo "run_worldweave=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| else | |
| BASE="HEAD~1" | |
| fi | |
| if ! git rev-parse "$BASE" &>/dev/null; then | |
| echo "run_frontend=true" >> "$GITHUB_OUTPUT" | |
| echo "run_api=true" >> "$GITHUB_OUTPUT" | |
| echo "run_agent_sdk=true" >> "$GITHUB_OUTPUT" | |
| echo "run_docker=true" >> "$GITHUB_OUTPUT" | |
| echo "run_worldweave=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| CHANGED=$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "") | |
| # Frontend: frontend/**, root config that affects frontend | |
| if echo "$CHANGED" | grep -qE '^frontend/|^vite\.config\.|^tsconfig\.json'; then | |
| echo "run_frontend=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run_frontend=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Backend API: backend/app/api, models, core, main, test_api | |
| if echo "$CHANGED" | grep -qE '^backend/(app/api/|app/models/|app/core/|main\.py|tests/test_api\.py|tests/conftest\.py)'; then | |
| echo "run_api=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run_api=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Backend Agent SDK: agent, prompts, skills, test_agent_sdk | |
| if echo "$CHANGED" | grep -qE '^backend/(app/agent/|app/prompts/|skills/|tests/test_agent_sdk\.py|tests/conftest\.py)'; then | |
| echo "run_agent_sdk=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run_agent_sdk=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Docker: docker-compose, Dockerfiles, deploy | |
| if echo "$CHANGED" | grep -qE '^docker-compose\.(yml|yaml)|^\.github/workflows/deploy\.yml|^scripts/deploy-clawarcade-reviewer\.sh|/(Dockerfile([^/]+)?|\.dockerignore)'; then | |
| echo "run_docker=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run_docker=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # WorldWeave: submodule pointer, deployment image, or same-origin proxy contract | |
| if echo "$CHANGED" | grep -qE '^worldweave$|^docker/worldweave\.Dockerfile|^\.github/workflows/ci\.yml|^frontend/(nginx(\.root)?\.conf|nginx\.conf\.template|vite\.config\.ts|src/pages/(SourceFeedPage\.tsx|__tests__/(WorldWeaveProxyConfig|ViteWorldWeaveProxyConfig|SourceFeedPage)\.test\.(ts|tsx)))$'; then | |
| echo "run_worldweave=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "run_worldweave=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: Frontend build (compile + typecheck) | |
| # ───────────────────────────────────────────────────────────────────────── | |
| frontend-build: | |
| name: Frontend build | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_frontend == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install and build | |
| run: | | |
| cd frontend | |
| npm ci | |
| npm run build | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: Frontend WorldWeave contract tests | |
| # ───────────────────────────────────────────────────────────────────────── | |
| frontend-worldweave-tests: | |
| name: Frontend WorldWeave tests | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_worldweave == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Run focused WorldWeave frontend tests | |
| run: | | |
| cd frontend | |
| npm ci | |
| npm test -- --run \ | |
| src/pages/__tests__/WorldWeaveProxyConfig.test.ts \ | |
| src/pages/__tests__/ViteWorldWeaveProxyConfig.test.ts \ | |
| src/pages/__tests__/SourceFeedPage.test.tsx | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: WorldWeave unit tests and build | |
| # ───────────────────────────────────────────────────────────────────────── | |
| worldweave-checks: | |
| name: WorldWeave checks | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_worldweave == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable pnpm | |
| working-directory: worldweave | |
| run: | | |
| corepack enable | |
| corepack prepare pnpm@9.0.0 --activate | |
| - name: Install dependencies | |
| working-directory: worldweave | |
| run: pnpm install --frozen-lockfile | |
| - name: Run unit tests | |
| working-directory: worldweave | |
| run: pnpm run test | |
| - name: Type check | |
| working-directory: worldweave | |
| run: pnpm run ts-check | |
| - name: Build | |
| working-directory: worldweave | |
| run: pnpm run build | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: Backend unit tests (API) | |
| # ───────────────────────────────────────────────────────────────────────── | |
| unit-tests-api: | |
| name: Unit (API) | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_api == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Run API unit tests | |
| working-directory: backend | |
| run: | | |
| echo "=== Unit tests: API (topics, posts, experts) ===" | |
| uv run \ | |
| --with fastapi --with uvicorn --with pydantic --with pydantic-settings \ | |
| --with python-dotenv --with claude-agent-sdk --with anthropic --with openai \ | |
| --with pytest --with pytest-asyncio --with httpx \ | |
| pytest tests/test_api.py -v --tb=short | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: Backend unit tests (Agent SDK, mocked) | |
| # ───────────────────────────────────────────────────────────────────────── | |
| unit-tests-agent-sdk: | |
| name: Unit (Agent SDK) | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_agent_sdk == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Setup uv | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Run Agent SDK unit tests (mocked) | |
| working-directory: backend | |
| run: | | |
| echo "=== Unit tests: Agent SDK (not integration) ===" | |
| uv run \ | |
| --with fastapi --with uvicorn --with pydantic --with pydantic-settings \ | |
| --with python-dotenv --with claude-agent-sdk --with anthropic --with openai \ | |
| --with pytest --with pytest-asyncio --with httpx \ | |
| pytest tests/test_agent_sdk.py -m "not integration" -v --tb=short | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 2: Integration tests (Agent SDK, real API) | |
| # Requires ANTHROPIC_API_KEY; skipped if not configured | |
| # ───────────────────────────────────────────────────────────────────────── | |
| integration-tests-agent-sdk: | |
| name: Integration (Agent SDK) | |
| needs: [detect-changes, unit-tests-agent-sdk] | |
| if: needs.detect-changes.outputs.run_agent_sdk == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Check ANTHROPIC_API_KEY | |
| id: check_key | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| if [[ -z "${ANTHROPIC_API_KEY:-}" || "${ANTHROPIC_API_KEY}" == "test" ]]; then | |
| echo "::notice title=Integration skipped::ANTHROPIC_API_KEY not configured; job skipped (success)" | |
| echo "has_key=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_key=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/checkout@v4 | |
| if: steps.check_key.outputs.has_key == 'true' | |
| with: | |
| submodules: recursive | |
| - name: Setup uv | |
| if: steps.check_key.outputs.has_key == 'true' | |
| uses: astral-sh/setup-uv@v5 | |
| - name: Setup Python | |
| if: steps.check_key.outputs.has_key == 'true' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Run Agent SDK integration tests | |
| if: steps.check_key.outputs.has_key == 'true' | |
| working-directory: backend | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: | | |
| uv run \ | |
| --with fastapi --with uvicorn --with pydantic --with pydantic-settings \ | |
| --with python-dotenv --with claude-agent-sdk --with anthropic --with openai \ | |
| --with pytest --with pytest-asyncio --with httpx \ | |
| pytest tests/test_agent_sdk.py -m integration -v --tb=short | |
| # ───────────────────────────────────────────────────────────────────────── | |
| # Layer 1: Docker build (validates deploy pipeline) | |
| # ───────────────────────────────────────────────────────────────────────── | |
| docker-build: | |
| name: Docker build | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.run_docker == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Build images | |
| run: | | |
| docker compose build --no-cache |