Skip to content

chore: migrate tooling to Ruff, expand CI, add Makefile and IaC workf… #6

chore: migrate tooling to Ruff, expand CI, add Makefile and IaC workf…

chore: migrate tooling to Ruff, expand CI, add Makefile and IaC workf… #6

Workflow file for this run

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'
run: |
infracost comment github \
--path=/tmp/infracost.json \
--repo=$GITHUB_REPOSITORY \
--github-token=${{ github.token }} \
--pull-request=${{ github.event.pull_request.number }} \
--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