chore: migrate tooling to Ruff, expand CI, add Makefile and IaC workf… #4
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 Pipeline | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| security-events: write | |
| env: | |
| PYTHON_VERSION: "3.12" | |
| TERRAFORM_VERSION: "1.7.0" | |
| jobs: | |
| # ────────────────────────────────────────────── | |
| # 1. CODE QUALITY GATES | |
| # ────────────────────────────────────────────── | |
| lint-and-format: | |
| name: Lint & Format | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install dependencies | |
| run: | | |
| pip install ruff mypy types-requests types-pyyaml | |
| - name: Run Ruff (linter + formatter check) | |
| run: | | |
| ruff check . --output-format=github | |
| ruff format --check . | |
| - name: Run mypy (type checking) | |
| run: mypy src/ app/ --ignore-missing-imports | |
| security-scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Run Bandit (Python security) | |
| run: | | |
| pip install bandit[toml] | |
| bandit -r src/ app/ -c pyproject.toml -f json -o bandit-report.json || true | |
| bandit -r src/ app/ -c pyproject.toml | |
| - name: Run Semgrep | |
| uses: semgrep/semgrep-action@v1 | |
| with: | |
| config: >- | |
| p/python | |
| p/security-audit | |
| p/secrets | |
| env: | |
| SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} | |
| - name: Dependency vulnerability check | |
| run: | | |
| pip install pip-audit | |
| pip-audit -r requirements-dev.txt --output json || true | |
| # ────────────────────────────────────────────── | |
| # 2. TESTING WITH COVERAGE GATE | |
| # ────────────────────────────────────────────── | |
| test: | |
| name: Test Suite | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install Poetry | |
| run: pip install poetry | |
| - name: Install dependencies | |
| run: poetry install --with dev | |
| - name: Run pytest with coverage | |
| run: | | |
| poetry run pytest tests/ \ | |
| --cov=src --cov=app \ | |
| --cov-report=xml:coverage.xml \ | |
| --cov-report=term-missing \ | |
| --cov-fail-under=80 \ | |
| -v | |
| - name: Upload coverage report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: coverage.xml | |
| # ────────────────────────────────────────────── | |
| # 3. INFRASTRUCTURE-AS-CODE VALIDATION | |
| # ────────────────────────────────────────────── | |
| terraform-validate: | |
| name: Terraform Validate & Security | |
| runs-on: ubuntu-latest | |
| if: | | |
| contains(github.event.pull_request.labels.*.name, 'infra') || | |
| github.event_name == 'push' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check for infra directory | |
| id: check_infra | |
| run: | | |
| if [ -d "infra" ]; then | |
| echo "has_infra=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_infra=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Setup Terraform | |
| if: steps.check_infra.outputs.has_infra == 'true' | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: ${{ env.TERRAFORM_VERSION }} | |
| - name: Terraform Format Check | |
| if: steps.check_infra.outputs.has_infra == 'true' | |
| run: terraform fmt -check -recursive infra/ | |
| - name: Terraform Init & Validate | |
| if: steps.check_infra.outputs.has_infra == 'true' | |
| run: | | |
| cd infra/ | |
| terraform init -backend=false | |
| terraform validate | |
| - name: Run Checkov (policy-as-code) | |
| if: steps.check_infra.outputs.has_infra == 'true' | |
| uses: bridgecrewio/checkov-action@v12 | |
| with: | |
| directory: infra/ | |
| framework: terraform | |
| output_format: cli,sarif | |
| output_file_path: console,checkov-results.sarif | |
| soft_fail: true | |
| - name: Run tfsec | |
| if: steps.check_infra.outputs.has_infra == 'true' | |
| uses: aquasecurity/tfsec-action@v1.0.3 | |
| with: | |
| working_directory: infra/ | |
| soft_fail: true | |
| - name: Infracost cost estimation | |
| if: github.event_name == 'pull_request' && steps.check_infra.outputs.has_infra == 'true' | |
| uses: infracost/actions/setup@v3 | |
| with: | |
| api-key: ${{ secrets.INFRACOST_API_KEY }} | |
| - name: Generate Infracost diff | |
| if: github.event_name == 'pull_request' && steps.check_infra.outputs.has_infra == 'true' | |
| run: | | |
| infracost diff \ | |
| --path=infra/ \ | |
| --format=json \ | |
| --out-file=/tmp/infracost.json | |
| - name: Post Infracost comment | |
| if: github.event_name == 'pull_request' && steps.check_infra.outputs.has_infra == 'true' | |
| uses: infracost/actions/comment@v3 | |
| with: | |
| path: /tmp/infracost.json | |
| behavior: update | |
| # ────────────────────────────────────────────── | |
| # 4. AI-POWERED PR REVIEW (optional) | |
| # ────────────────────────────────────────────── | |
| ai-review: | |
| name: AI Code Review | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: CodeRabbit AI Review | |
| uses: coderabbitai/ai-pr-reviewer@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| with: | |
| debug: false | |
| review_simple_changes: false | |
| review_comment_lgtm: false |