name: Service Adapters (Python FastAPI) on: push: paths: - 'services/service-adapters/**' - '.gitea/workflows/service-adapters.yml' pull_request: paths: - 'services/service-adapters/**' workflow_dispatch: inputs: run_tests: description: 'Run tests' required: false default: true type: boolean run_lint: description: 'Run linting' required: false default: true type: boolean run_build: description: 'Run build' required: false default: true type: boolean run_sonar: description: 'Run SonarQube analysis' required: false default: true type: boolean env: REGISTRY: gitea.example.com IMAGE_PREFIX: labusion SERVICE_NAME: service-adapters jobs: test: runs-on: [self-hosted] env: RUNNER_TOOL_CACHE: /toolcache defaults: run: working-directory: ./services/service-adapters strategy: matrix: python-version: [3.11, 3.12, 3.13] steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Cache pip dependencies uses: actions/cache@v4 with: path: | ~/.cache/pip ~/.local/lib/python${{ matrix.python-version }}/site-packages key: pip-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/requirements.txt') }} restore-keys: | pip-${{ runner.os }}-${{ matrix.python-version }}- pip-${{ runner.os }}- pip- fail-on-cache-miss: false id: pip-cache - name: Cache status run: | if [ "${{ steps.pip-cache.outputs.cache-hit }}" == "true" ]; then echo "✅ Cache hit! Dependencies will be restored from cache." else echo "❌ Cache miss. Dependencies will be downloaded fresh." fi - name: Install dependencies run: | python -m pip install --upgrade pip pip install --cache-dir ~/.cache/pip -r requirements.txt pip install --cache-dir ~/.cache/pip pytest pytest-cov pytest-asyncio httpx pip install --cache-dir ~/.cache/pip flake8 black isort mypy bandit safety - name: Run code formatting check run: | black --check --diff . isort --check-only --diff --profile black . - name: Run linting run: | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics flake8 . --count --max-complexity=10 --max-line-length=150 --statistics - name: Run type checking run: mypy . --ignore-missing-imports - name: Run security checks run: | bandit -r . -f json -o bandit-report.json --severity-level medium safety check --json > safety-report.json || echo "Safety check completed with warnings" - name: Create test reports directory run: | mkdir -p tests/reports - name: Run tests run: | pytest --cov=. --cov-report=xml --cov-report=html --cov-report=term-missing --junitxml=tests/reports/junit.xml --cov-fail-under=80 - name: Send results to SonarQube run: | echo "Sending Service Adapters results to SonarQube..." # Install pysonar for SonarQube analysis pip install pysonar # Run SonarQube analysis pysonar \ --sonar-host-url=${{ secrets.SONAR_HOST_URL }} \ --sonar-token=${{ secrets.SONAR_TOKEN }} \ --sonar-project-key=labfusion-service-adapters \ --sonar-project-name="LabFusion Service Adapters" \ --sonar-python-coverage-report-paths=coverage.xml \ --sonar-sources=. \ --sonar-exclusions=tests/**,htmlcov/**,__pycache__/**,*.pyc - name: Test results summary if: always() run: | echo "Test results available in pipeline logs" echo "Coverage report: services/service-adapters/coverage.xml" echo "HTML coverage: services/service-adapters/htmlcov/" echo "Security reports: bandit-report.json, safety-report.json" build: runs-on: [self-hosted] needs: test defaults: run: working-directory: ./services/service-adapters steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: '3.11' - name: Cache pip dependencies uses: actions/cache@v4 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: ${{ runner.os }}-pip - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Build Docker image (test only) run: docker build -t service-adapters:test .