diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5e180ee --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: CI + +on: + pull_request: + branches: + - main + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Install struct-ia + run: | + pip install git+https://github.com/struct-ai/struct-ai.git@feat/20-github-action-analyse-pr + + - name: Verify Python sources + run: python -m compileall -q src diff --git a/.github/workflows/pr_analysis.yml b/.github/workflows/pr_analysis.yml new file mode 100644 index 0000000..789d76c --- /dev/null +++ b/.github/workflows/pr_analysis.yml @@ -0,0 +1,52 @@ +name: PR Analysis + +on: + pull_request: + branches: + - main + types: + - opened + - synchronize + - reopened + +# Minimal permissions: read code, write PR comments. +permissions: + contents: read + pull-requests: write + +jobs: + struct-ia-review: + name: struct-ia — Clean Architecture Analysis + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + # Full history required for git diff against origin/main + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: pip install "git+https://github.com/struct-ai/struct-ai.git@feat/20-github-action-analyse-pr" + + - name: Run struct-ia PR analysis + run: python -m struct_ai.entrypoints.github_action.main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + PR_NUMBER: ${{ github.event.number }} + GITHUB_WORKSPACE: ${{ github.workspace }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + # Uncomment the provider you want to use instead of OpenAI: + # ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + # GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} + # MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }} + # OLLAMA_BASE_URL: ${{ secrets.OLLAMA_BASE_URL }} + # + # Set to "false" to make violations warnings-only (CI won't fail). + STRUCT_IA_FAIL_ON_VIOLATION: "true" diff --git a/.struct-ia.yaml b/.struct-ia.yaml index 4a706b3..3fbff33 100644 --- a/.struct-ia.yaml +++ b/.struct-ia.yaml @@ -19,3 +19,10 @@ layers: - name: entrypoints paths: - entrypoints + +# Démonstration struct-ia : fichiers avec layer_violation volontaire (imports inter-couches). +# - domain/model.py, domain/ports.py → import infrastructure +# - domain/settings_mirror.py → import entrypoints +# - infrastructure/repository.py, infrastructure/cli_bridge.py → import entrypoints + +provider: openai diff --git a/src/app/domain/ports.py b/src/app/domain/ports.py new file mode 100644 index 0000000..945bd3f --- /dev/null +++ b/src/app/domain/ports.py @@ -0,0 +1,8 @@ +"""Intentional violation: domain re-declares infrastructure coupling.""" + +# struct-ia: expect layer_violation — domain imports infrastructure. +from app.infrastructure.repository import SqlUserRepository + + +def make_repository() -> SqlUserRepository: + return SqlUserRepository() diff --git a/src/app/domain/settings_mirror.py b/src/app/domain/settings_mirror.py new file mode 100644 index 0000000..d1afdf7 --- /dev/null +++ b/src/app/domain/settings_mirror.py @@ -0,0 +1,8 @@ +"""Intentional violation: domain must not depend on entrypoints (outer layer).""" + +# struct-ia: expect layer_violation — domain imports entrypoints. +from app.entrypoints.main import APP_CONFIG + + +def get_configured_db_path() -> str: + return str(APP_CONFIG.get("db_path", "db/users.json")) diff --git a/src/app/infrastructure/cli_bridge.py b/src/app/infrastructure/cli_bridge.py new file mode 100644 index 0000000..0db611d --- /dev/null +++ b/src/app/infrastructure/cli_bridge.py @@ -0,0 +1,10 @@ +"""Intentional violation: infrastructure must not depend on entrypoints.""" + +from typing import Any, Dict + +# struct-ia: expect layer_violation — infrastructure imports entrypoints. +from app.entrypoints.main import create_user_from_http_payload + + +def bootstrap_user_from_cli_payload(payload: Dict[str, Any]) -> None: + create_user_from_http_payload(payload)