chore: trigger deploy — EC2 rebooted, IAM role active, bucket=amzn-saas3 #45
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/CD | |
| # Cancel any in-progress run for the same branch when a new push arrives. | |
| # Prevents two pipeline runs from piling up on rapid commits. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| branches: [ main ] | |
| jobs: | |
| # ── Detect which parts of the codebase changed ───────────────────────────── | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| backend: ${{ steps.filter.outputs.backend }} | |
| frontend: ${{ steps.filter.outputs.frontend }} | |
| infra: ${{ steps.filter.outputs.infra }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| backend: | |
| - 'backend/**' | |
| frontend: | |
| - 'frontend/**' | |
| infra: | |
| - 'scripts/**' | |
| - 'nginx/**' | |
| - 'docker-compose.prod.yml' | |
| # ── Run Java tests (only when backend code changed) ──────────────────────── | |
| test: | |
| name: Test | |
| needs: changes | |
| if: needs.changes.outputs.backend == 'true' | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:15-alpine | |
| env: | |
| POSTGRES_DB: saas_test | |
| POSTGRES_USER: saas_user | |
| POSTGRES_PASSWORD: saas_password | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd pg_isready | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| redis: | |
| image: redis:7-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Java 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' | |
| distribution: temurin | |
| cache: maven | |
| - name: Run tests | |
| working-directory: backend | |
| env: | |
| SPRING_PROFILES_ACTIVE: test | |
| SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/saas_test | |
| SPRING_DATASOURCE_USERNAME: saas_user | |
| SPRING_DATASOURCE_PASSWORD: saas_password | |
| SPRING_DATA_REDIS_HOST: localhost | |
| SPRING_DATA_REDIS_PORT: '6379' | |
| AWS_ACCESS_KEY_ID: test | |
| AWS_SECRET_ACCESS_KEY: test | |
| AWS_REGION: ap-south-1 | |
| JWT_SECRET: dGVzdC1qd3Qtc2VjcmV0LW11c3QtYmUtbG9uZy1lbm91Z2gtZm9yLUhTMjU2LWFsZ29yaXRobQ== | |
| run: mvn test -B --no-transfer-progress -T 1C | |
| - name: Upload test reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-reports | |
| path: backend/target/surefire-reports/ | |
| retention-days: 7 | |
| # ── Build & push backend Docker image ────────────────────────────────────── | |
| build: | |
| name: Build and Push Backend | |
| runs-on: ubuntu-latest | |
| needs: [changes, test] | |
| if: | | |
| always() && | |
| github.event_name == 'push' && github.ref == 'refs/heads/main' && | |
| needs.changes.outputs.backend == 'true' && | |
| needs.test.result == 'success' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Java 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' | |
| distribution: temurin | |
| cache: maven | |
| - name: Build JAR | |
| working-directory: backend | |
| run: mvn package -DskipTests -B --no-transfer-progress -T 1C | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Build and push | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./backend | |
| file: ./backend/Dockerfile.ci | |
| push: true | |
| tags: | | |
| ${{ secrets.DOCKER_USERNAME }}/saas-backend:latest | |
| ${{ secrets.DOCKER_USERNAME }}/saas-backend:${{ github.sha }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # ── Build & push frontend Docker image ───────────────────────────────────── | |
| build-frontend: | |
| name: Build and Push Frontend | |
| runs-on: ubuntu-latest | |
| needs: [changes, test] | |
| if: | | |
| always() && | |
| github.event_name == 'push' && github.ref == 'refs/heads/main' && | |
| needs.changes.outputs.frontend == 'true' && | |
| (needs.test.result == 'success' || needs.test.result == 'skipped') | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Build and push | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./frontend | |
| file: ./frontend/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ secrets.DOCKER_USERNAME }}/saas-frontend:latest | |
| ${{ secrets.DOCKER_USERNAME }}/saas-frontend:${{ github.sha }} | |
| build-args: | | |
| VITE_API_BASE_URL= | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # ── Deploy to EC2 ──────────────────────────────────────────────────────────── | |
| deploy: | |
| name: Deploy to EC2 | |
| runs-on: ubuntu-latest | |
| needs: [changes, build, build-frontend] | |
| if: | | |
| always() && | |
| github.event_name == 'push' && github.ref == 'refs/heads/main' && | |
| (needs.build.result == 'success' || needs.build.result == 'skipped') && | |
| (needs.build-frontend.result == 'success' || needs.build-frontend.result == 'skipped') && | |
| needs.build.result != 'failure' && | |
| needs.build-frontend.result != 'failure' && | |
| ( | |
| needs.changes.outputs.backend == 'true' || | |
| needs.changes.outputs.frontend == 'true' || | |
| needs.changes.outputs.infra == 'true' | |
| ) | |
| environment: production | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up SSH | |
| run: | | |
| mkdir -p ~/.ssh | |
| printf '%s\n' "${{ secrets.EC2_SSH_KEY }}" > ~/.ssh/deploy_key | |
| chmod 600 ~/.ssh/deploy_key | |
| echo "StrictHostKeyChecking no" >> ~/.ssh/config | |
| echo "UserKnownHostsFile /dev/null" >> ~/.ssh/config | |
| - name: Deploy | |
| env: | |
| DOCKER_IMAGE: ${{ secrets.DOCKER_USERNAME }}/saas-backend | |
| DOCKER_FRONTEND_IMAGE: ${{ secrets.DOCKER_USERNAME }}/saas-frontend | |
| DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} | |
| DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} | |
| IMAGE_TAG: ${{ github.sha }} | |
| EC2_HOST: ${{ secrets.EC2_HOST }} | |
| EC2_USER: ${{ secrets.EC2_USER }} | |
| run: SSH_KEY=~/.ssh/deploy_key bash scripts/deploy.sh |