web: v1.1.1 #63
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 - Continuous Integration | |
| on: | |
| push: | |
| branches: | |
| - production | |
| - develop | |
| - 'feature/**' | |
| paths: | |
| - 'apps/web/**' | |
| - 'apps/api/**' | |
| - 'apps/api-springboot/**' | |
| - 'infra/**' | |
| - 'ansible/**' | |
| - 'terraform/**' | |
| - 'monitoring/**' | |
| - 'docker-compose.yml' | |
| - '.github/workflows/**' | |
| pull_request: | |
| branches: | |
| - production | |
| - develop | |
| paths: | |
| - 'apps/web/**' | |
| - 'apps/api/**' | |
| - 'apps/api-springboot/**' | |
| - 'infra/**' | |
| - 'ansible/**' | |
| - 'terraform/**' | |
| - 'monitoring/**' | |
| - 'docker-compose.yml' | |
| - '.github/workflows/**' | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| env: | |
| NODE_VERSION: '24.3.0' | |
| JAVA_VERSION: '21' | |
| jobs: | |
| # ============================================ | |
| # Detect Changes | |
| # ============================================ | |
| changes: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| outputs: | |
| web: ${{ steps.filter.outputs.web }} | |
| api: ${{ steps.filter.outputs.api }} | |
| springboot: ${{ steps.filter.outputs.springboot }} | |
| infra: ${{ steps.filter.outputs.infra }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| web: | |
| - 'apps/web/**' | |
| - '.github/workflows/**' | |
| api: | |
| - 'apps/api/**' | |
| - '.github/workflows/**' | |
| springboot: | |
| - 'apps/api-springboot/**' | |
| - '.github/workflows/**' | |
| infra: | |
| - 'infra/**' | |
| - 'ansible/**' | |
| - 'terraform/**' | |
| - 'monitoring/**' | |
| - 'docker-compose.yml' | |
| - '.github/workflows/**' | |
| # ============================================ | |
| # Web (Next.js) CI | |
| # ============================================ | |
| web-ci: | |
| name: Web - Build & Test | |
| needs: changes | |
| if: ${{ needs.changes.outputs.web == 'true' }} | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: apps/web | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: | | |
| if npm ci 2>/dev/null; then | |
| echo "✅ npm ci succeeded" | |
| else | |
| echo "⚠️ npm ci failed, falling back to npm install" | |
| npm install | |
| fi | |
| - name: Run linter | |
| run: npm run lint | |
| - name: Type check | |
| run: npx tsc --noEmit | |
| - name: Build application | |
| run: npm run build | |
| env: | |
| NEXT_PUBLIC_API_URL: http://localhost:3001 | |
| - name: Run tests | |
| run: npm test -- --passWithNoTests | |
| # ============================================ | |
| # API NestJS CI | |
| # ============================================ | |
| api-nestjs-ci: | |
| name: API NestJS - Build & Test | |
| needs: changes | |
| if: ${{ needs.changes.outputs.api == 'true' }} | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: apps/api | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: flowly_test | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| redis: | |
| image: redis:7 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 6379:6379 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: | | |
| if npm ci 2>/dev/null; then | |
| echo "✅ npm ci succeeded" | |
| else | |
| echo "⚠️ npm ci failed, falling back to npm install" | |
| npm install | |
| fi | |
| - name: Run linter | |
| run: npm run lint | |
| - name: Type check | |
| run: npx tsc --noEmit | |
| - name: Build application | |
| run: npm run build | |
| - name: Run tests with coverage | |
| run: npm run test:cov | |
| env: | |
| DATABASE_URL: postgresql://postgres:postgres@localhost:5432/flowly_test | |
| REDIS_HOST: localhost | |
| REDIS_PORT: 6379 | |
| JWT_SECRET: test-secret-key | |
| - name: Check coverage threshold | |
| run: | | |
| COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct') | |
| echo "Coverage: $COVERAGE%" | |
| if (( $(echo "$COVERAGE < 80" | bc -l) )); then | |
| echo "❌ Coverage is below 80%" | |
| exit 1 | |
| fi | |
| echo "✅ Coverage meets 80% threshold" | |
| - name: Upload coverage reports | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./apps/api/coverage/lcov.info | |
| flags: api-nestjs | |
| fail_ci_if_error: false | |
| # ============================================ | |
| # API Spring Boot CI | |
| # ============================================ | |
| api-springboot-ci: | |
| name: API Spring Boot - Build & Test | |
| needs: changes | |
| if: ${{ needs.changes.outputs.springboot == 'true' }} | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: apps/api-springboot | |
| services: | |
| postgres: | |
| image: postgres:16 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: postgres | |
| POSTGRES_DB: flowly_test | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 5432:5432 | |
| redis: | |
| image: redis:7 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| ports: | |
| - 6379:6379 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: ${{ env.JAVA_VERSION }} | |
| cache: 'gradle' | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Run tests with coverage | |
| run: ./gradlew test jacocoTestReport | |
| env: | |
| SPRING_PROFILES_ACTIVE: ci | |
| JWT_SECRET: your_very_secure_and_long_secret_key_for_jwt_signing_at_least_32_bytes | |
| - name: Check coverage threshold | |
| run: ./gradlew jacocoTestCoverageVerification | |
| continue-on-error: true | |
| - name: Build application | |
| run: ./gradlew assemble | |
| - name: Upload coverage reports | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./apps/api-springboot/build/reports/jacoco/test/jacocoTestReport.xml | |
| flags: api-springboot | |
| fail_ci_if_error: false | |
| # ============================================ | |
| # Infrastructure - Terraform & Ansible | |
| # ============================================ | |
| infra-ci: | |
| name: Infrastructure - Validate | |
| needs: changes | |
| if: ${{ needs.changes.outputs.infra == 'true' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Terraform | |
| uses: hashicorp/setup-terraform@v3 | |
| with: | |
| terraform_version: 1.5.0 | |
| - name: Terraform Format Check (dev-with-ansible) | |
| working-directory: terraform/environments/dev-with-ansible | |
| run: terraform fmt -check -recursive | |
| - name: Terraform Init (dev-with-ansible) | |
| working-directory: terraform/environments/dev-with-ansible | |
| run: terraform init -backend=false | |
| - name: Terraform Validate (dev-with-ansible) | |
| working-directory: terraform/environments/dev-with-ansible | |
| run: terraform validate | |
| - name: Setup Python for Ansible | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Ansible and dependencies | |
| run: | | |
| pip install ansible ansible-lint boto3 botocore checkov | |
| - name: Ansible Lint (AWS dev environment) | |
| working-directory: ansible/aws | |
| run: | | |
| ansible-lint playbooks/*.yml || true | |
| - name: Ansible Lint (on-premise environment) | |
| working-directory: ansible/on-premise | |
| run: | | |
| ansible-lint playbooks/*.yml || true | |
| - name: Run Checkov IaC scan | |
| run: | | |
| checkov --directory terraform/ \ | |
| --framework terraform \ | |
| --output sarif \ | |
| --output-file-path checkov-results.sarif \ | |
| --soft-fail | |
| continue-on-error: true | |
| - name: Upload Checkov results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: checkov-results.sarif | |
| category: 'checkov-iac' | |
| # ============================================ | |
| # Security Scanning - Trivy | |
| # ============================================ | |
| trivy-scan: | |
| name: Security - Trivy Scan | |
| runs-on: ubuntu-latest | |
| needs: [web-ci, api-nestjs-ci, api-springboot-ci] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Trivy vulnerability scanner (web) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: 'apps/web' | |
| format: 'sarif' | |
| output: 'trivy-web-results.sarif' | |
| - name: Upload Trivy results (web) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'trivy-web-results.sarif' | |
| category: 'trivy-web' | |
| - name: Run Trivy vulnerability scanner (api-nestjs) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: 'apps/api' | |
| format: 'sarif' | |
| output: 'trivy-api-nestjs-results.sarif' | |
| - name: Upload Trivy results (api-nestjs) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'trivy-api-nestjs-results.sarif' | |
| category: 'trivy-api-nestjs' | |
| - name: Run Trivy vulnerability scanner (api-springboot) | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: 'apps/api-springboot' | |
| format: 'sarif' | |
| output: 'trivy-api-springboot-results.sarif' | |
| - name: Upload Trivy results (api-springboot) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'trivy-api-springboot-results.sarif' | |
| category: 'trivy-api-springboot' | |
| # ============================================ | |
| # Security Scanning - Snyk | |
| # ============================================ | |
| snyk-scan: | |
| name: Security - Snyk Scan | |
| runs-on: ubuntu-latest | |
| needs: [web-ci, api-nestjs-ci, api-springboot-ci] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: ${{ env.JAVA_VERSION }} | |
| - name: Run Snyk to check for vulnerabilities (web) | |
| uses: snyk/actions/node@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --sarif-file-output=snyk-web.sarif --file=apps/web/package.json | |
| command: test | |
| - name: Upload Snyk results (web) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'snyk-web.sarif' | |
| category: 'snyk-web' | |
| - name: Run Snyk to check for vulnerabilities (api-nestjs) | |
| uses: snyk/actions/node@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --sarif-file-output=snyk-api-nestjs.sarif --file=apps/api/package.json | |
| command: test | |
| - name: Upload Snyk results (api-nestjs) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'snyk-api-nestjs.sarif' | |
| category: 'snyk-api-nestjs' | |
| - name: Run Snyk to check for vulnerabilities (api-springboot) | |
| uses: snyk/actions/gradle@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --sarif-file-output=snyk-api-springboot.sarif --file=apps/api-springboot/build.gradle | |
| command: test | |
| - name: Upload Snyk results (api-springboot) | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: 'snyk-api-springboot.sarif' | |
| category: 'snyk-api-springboot' | |
| # ============================================ | |
| # Security Scanning - Semgrep | |
| # ============================================ | |
| semgrep-scan: | |
| name: Security - Semgrep Scan | |
| runs-on: ubuntu-latest | |
| needs: [web-ci, api-nestjs-ci, api-springboot-ci] | |
| container: | |
| image: semgrep/semgrep | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Semgrep scan | |
| run: | | |
| semgrep scan \ | |
| --config auto \ | |
| --sarif \ | |
| --output semgrep-results.sarif \ | |
| --verbose | |
| env: | |
| SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} | |
| continue-on-error: true | |
| - name: Upload Semgrep results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| continue-on-error: true | |
| with: | |
| sarif_file: semgrep-results.sarif | |
| category: 'semgrep' | |
| # ============================================ | |
| # Summary | |
| # ============================================ | |
| ci-summary: | |
| name: CI Summary | |
| runs-on: ubuntu-latest | |
| needs: [web-ci, api-nestjs-ci, api-springboot-ci, infra-ci, trivy-scan, snyk-scan, semgrep-scan] | |
| if: always() | |
| steps: | |
| - name: Check CI Results | |
| run: | | |
| echo "🎯 CI Pipeline Results:" | |
| echo "========================" | |
| echo "Web CI: ${{ needs.web-ci.result }}" | |
| echo "API NestJS CI: ${{ needs.api-nestjs-ci.result }}" | |
| echo "API Spring Boot CI: ${{ needs.api-springboot-ci.result }}" | |
| echo "Infrastructure CI: ${{ needs.infra-ci.result }}" | |
| echo "========================" | |
| echo "Security Scans:" | |
| echo " Trivy: ${{ needs.trivy-scan.result }}" | |
| echo " Snyk: ${{ needs.snyk-scan.result }}" | |
| echo " Semgrep: ${{ needs.semgrep-scan.result }}" | |
| echo " Checkov: included in infra-ci" | |
| echo "========================" | |
| if [[ "${{ needs.web-ci.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.api-nestjs-ci.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.api-springboot-ci.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.infra-ci.result }}" == "failure" ]]; then | |
| echo "❌ CI Pipeline Failed" | |
| exit 1 | |
| fi | |
| echo "✅ All CI Checks Passed" | |
| echo "ℹ️ Security scans are informational (continue-on-error enabled)" |