--- name: CI on: push: branches: [main, qa, dev] pull_request: branches: [main, qa, dev] jobs: lint-and-test: runs-on: ubuntu-latest container: image: python:3.11-bullseye services: postgres: image: postgres:15 env: POSTGRES_USER: poteuser POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD || 'testpass123' }} POSTGRES_DB: potedb_test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Check out code uses: actions/checkout@v4 - name: Install system dependencies run: | apt-get update apt-get install -y postgresql-client - name: Install Python dependencies run: | pip install --upgrade pip pip install -e ".[dev]" - name: Run linters run: | echo "Running ruff..." ruff check src/ tests/ || true echo "Running black check..." black --check src/ tests/ || true echo "Running mypy..." mypy src/ --install-types --non-interactive || true - name: Run tests with coverage env: DATABASE_URL: postgresql://poteuser:${{ secrets.DB_PASSWORD || 'testpass123' }}@postgres:5432/potedb_test SMTP_HOST: ${{ secrets.SMTP_HOST || 'localhost' }} SMTP_PORT: 587 SMTP_USER: ${{ secrets.SMTP_USER || 'test@example.com' }} SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD || 'dummy' }} FROM_EMAIL: ${{ secrets.FROM_EMAIL || 'test@example.com' }} run: | pytest tests/ -v --cov=src/pote --cov-report=term --cov-report=xml - name: Test scripts env: DATABASE_URL: postgresql://poteuser:${{ secrets.DB_PASSWORD || 'testpass123' }}@postgres:5432/potedb_test run: | echo "Testing database migrations..." alembic upgrade head echo "Testing price loader..." python scripts/fetch_sample_prices.py || true secret-scanning: runs-on: ubuntu-latest container: image: zricethezav/gitleaks:latest steps: - name: Install Node.js for checkout action run: | apk add --no-cache nodejs npm curl git - name: Check out code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Scan for secrets run: | echo "๐Ÿ” Scanning for exposed secrets..." gitleaks detect --source . --no-banner --redact --exit-code 0 || true continue-on-error: true security-scan: runs-on: ubuntu-latest container: image: python:3.11-bullseye steps: - name: Check out code uses: actions/checkout@v4 - name: Install dependencies run: | pip install --upgrade pip pip install safety bandit - name: Run safety check run: | pip install -e . echo "๐Ÿ” Checking for known vulnerabilities in dependencies..." safety check --json || true continue-on-error: true - name: Run bandit security scan run: | echo "๐Ÿ” Running static security analysis..." bandit -r src/ -f json -o bandit-report.json || true bandit -r src/ -f screen continue-on-error: true dependency-scan: runs-on: ubuntu-latest container: image: aquasec/trivy:latest steps: - name: Install Node.js for checkout action run: | apk add --no-cache nodejs npm curl git - name: Check out code uses: actions/checkout@v4 - name: Scan dependencies run: | echo "๐Ÿ” Scanning dependencies for vulnerabilities..." trivy fs --scanners vuln --exit-code 0 . sast-scan: runs-on: ubuntu-latest container: image: ubuntu:22.04 steps: - name: Install Node.js for checkout action run: | apt-get update && apt-get install -y curl git curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y nodejs - name: Check out code uses: actions/checkout@v4 - name: Install Semgrep run: | apt-get update && apt-get install -y python3 python3-pip pip3 install semgrep - name: Run Semgrep scan run: | echo "๐Ÿ” Running SAST analysis with Semgrep..." semgrep --config=auto --error || true continue-on-error: true container-scan: runs-on: ubuntu-latest container: image: ubuntu:22.04 steps: - name: Install Node.js for checkout action run: | apt-get update && apt-get install -y curl git curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y nodejs - name: Check out code uses: actions/checkout@v4 - name: Install Trivy run: | set -e apt-get update && apt-get install -y wget curl tar # Use a fixed, known-good Trivy version TRIVY_VERSION="0.58.2" TRIVY_URL="https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" echo "Installing Trivy version: ${TRIVY_VERSION}" if ! wget --progress=bar:force "${TRIVY_URL}" -O /tmp/trivy.tar.gz 2>&1; then echo "โŒ Failed to download Trivy" exit 1 fi if [ ! -f /tmp/trivy.tar.gz ] || [ ! -s /tmp/trivy.tar.gz ]; then echo "โŒ Downloaded Trivy archive is missing or empty" exit 1 fi echo "Extracting Trivy..." if ! tar -xzf /tmp/trivy.tar.gz -C /tmp/ trivy; then echo "โŒ Failed to extract Trivy" exit 1 fi mv /tmp/trivy /usr/local/bin/trivy chmod +x /usr/local/bin/trivy trivy --version - name: Scan Dockerfile run: | if [ -f "Dockerfile" ]; then echo "๐Ÿ” Scanning Dockerfile for vulnerabilities..." trivy config Dockerfile || true else echo "No Dockerfile found, skipping scan" fi continue-on-error: true - name: Scan filesystem run: | echo "๐Ÿ” Scanning filesystem for vulnerabilities..." trivy fs --scanners vuln --severity HIGH,CRITICAL --format table . || true continue-on-error: true docker-build-test: runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build Docker image uses: docker/build-push-action@v5 with: context: . push: false tags: pote:test cache-from: type=gha cache-to: type=gha,mode=max - name: Test Docker image run: | docker run --rm pote:test python -c "import pote; print('โœ… POTE import successful')" workflow-summary: runs-on: ubuntu-latest needs: [lint-and-test, secret-scanning, security-scan, dependency-scan, sast-scan, container-scan, docker-build-test] if: always() steps: - name: Generate workflow summary run: | echo "## ๐Ÿ” CI Workflow Summary" >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true echo "### Job Results" >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY || true echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿงช Lint & Test | ${{ needs.lint-and-test.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿ” Secret Scanning | ${{ needs.secret-scanning.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿ”’ Security Scan | ${{ needs.security-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿ“ฆ Dependency Scan | ${{ needs.dependency-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿ” SAST Scan | ${{ needs.sast-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿณ Container Scan | ${{ needs.container-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "| ๐Ÿ‹ Docker Build | ${{ needs.docker-build-test.result }} |" >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true echo "### ๐Ÿ“Š Summary" >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true echo "All security and validation checks have completed." >> $GITHUB_STEP_SUMMARY || true echo "" >> $GITHUB_STEP_SUMMARY || true echo "**Security Layers:**" >> $GITHUB_STEP_SUMMARY || true echo "- โœ… Secret scanning (Gitleaks)" >> $GITHUB_STEP_SUMMARY || true echo "- โœ… Dependency vulnerabilities (Safety + Trivy)" >> $GITHUB_STEP_SUMMARY || true echo "- โœ… Static security analysis (Bandit)" >> $GITHUB_STEP_SUMMARY || true echo "- โœ… SAST scanning (Semgrep)" >> $GITHUB_STEP_SUMMARY || true echo "- โœ… Container scanning (Trivy)" >> $GITHUB_STEP_SUMMARY || true continue-on-error: true