Merge pull request #66 from prgrms-web-devcourse-final-project/chore/… #111
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_Pipeline | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| on: | |
| push: | |
| branches: [ main, develop ] | |
| pull_request: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| jobs: | |
| tests: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ ubuntu-latest ] | |
| include: | |
| - os: ubuntu-latest | |
| gradle_cmd: "./gradlew" | |
| report_path: "backend/build/reports/tests" | |
| domain_tasks: "testUser testExchange testTrade_log testWallet testCoin" | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| SPRING_PROFILES_ACTIVE: test-ci | |
| # ✅ Redis 서비스 추가 | |
| services: | |
| redis: | |
| image: redis:7-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| env: | |
| REDIS_PASSWORD: "" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '21' | |
| distribution: 'temurin' | |
| cache: gradle | |
| # ✅ gradlew 실행 권한 부여 | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x backend/gradlew | |
| # ✅ Redis 연결 테스트 | |
| - name: Test Redis connection | |
| run: | | |
| echo "Testing Redis connection..." | |
| timeout 10s bash -c 'until printf "" 2>>/dev/null >>/dev/tcp/localhost/6379; do sleep 1; done' | |
| echo "Redis is ready!" | |
| # ✅ application-test.yml에서 사용하는 모든 환경변수를 .env 파일에 생성 | |
| - name: Create test .env file | |
| working-directory: backend | |
| run: | | |
| cat > .env << 'EOF' | |
| # Datasource 설정 (application-test.yml에서 참조) | |
| TEST_DATASOURCE_URL=jdbc:h2:mem:db_test;MODE=MySQL | |
| TEST_DATASOURCE_USERNAME=sa | |
| TEST_DATASOURCE_PASSWORD= | |
| TEST_DATASOURCE_DRIVER=org.h2.Driver | |
| # JPA 설정 (application-test.yml에서 참조) | |
| TEST_JPA_HIBERNATE_DDL_AUTO=create-drop | |
| # Redis 설정 (application-test.yml에서 참조, GitHub Actions 서비스 사용) | |
| TEST_REDIS_HOST=localhost | |
| TEST_REDIS_PORT=6379 | |
| TEST_REDIS_PASSWORD= | |
| # CI/CD 환경에서는 Embedded Redis 끄기 | |
| SPRING_DATA_REDIS_EMBEDDED=false | |
| # JWT 설정 (application-test.yml에서 참조) | |
| CUSTOM_JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }} | |
| CUSTOM_JWT_ACCESS_TOKEN_EXPIRATION_SECONDS=3600 | |
| EOF | |
| - name: Run unit, and domain tests | |
| run: ${{ matrix.gradle_cmd }} clean test | |
| working-directory: backend | |
| - name: Upload Test Reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-reports-${{ matrix.os }} | |
| path: ${{ matrix.report_path }} | |
| retention-days: 7 | |
| build-artifacts: | |
| needs: tests | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' # ✅ main 브랜치일 때만 실행 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 21 | |
| cache: gradle | |
| # ✅ gradlew 실행 권한 부여 | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x backend/gradlew | |
| # ✅ 빌드용 .env 파일 생성 (Configuration Properties 바인딩용 최소 환경변수만) | |
| - name: Create build .env file | |
| working-directory: backend | |
| run: | | |
| cat > .env << 'EOF' | |
| # JWT Configuration Properties 바인딩용 (빌드 시 필요) | |
| CUSTOM_JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }} | |
| CUSTOM_JWT_ACCESS_TOKEN_EXPIRATION_SECONDS=3600 | |
| EOF | |
| - name: Gradle bootJar | |
| working-directory: backend | |
| run: ./gradlew --no-daemon clean bootJar -x test | |
| - name: Copy JAR to dist | |
| working-directory: backend | |
| run: | | |
| mkdir -p dist | |
| cp $(ls build/libs/*.jar | grep -v plain | head -n 1) dist/app.jar | |
| - name: Upload backend jar | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: backend-jar | |
| path: backend/dist/app.jar | |
| docker-build: | |
| needs: build-artifacts | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' # ✅ main 브랜치일 때만 실행 | |
| env: | |
| REGISTRY: ghcr.io | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set lowercase repo name | |
| run: | | |
| echo "IMAGE_PREFIX=$(echo $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | |
| - name: Download backend jar | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: backend-jar | |
| path: backend/dist | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push backend (runtime-only) | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: backend | |
| file: backend/Dockerfile | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/balaw:${{ github.sha }} | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/balaw:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| deploy: | |
| needs: docker-build | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' # ✅ main 브랜치일 때만 실행 | |
| env: | |
| DOCKER_IMAGE_NAME: balaw | |
| REGISTRY: ghcr.io | |
| steps: | |
| - name: Set lowercase repo name | |
| run: | | |
| echo "IMAGE_PREFIX=$(echo $GITHUB_REPOSITORY | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV | |
| - name: Create prod .env file | |
| run: | | |
| cat > .env << 'EOF' | |
| SPRING_PROFILES_ACTIVE=prod | |
| PROD_DATASOURCE_URL=jdbc:mysql://mysql_1:3306/${{ secrets.DB_NAME }} | |
| PROD_DATASOURCE_USERNAME=${{ secrets.DB_USER }} | |
| PROD_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| PROD_REDIS_HOST=redis_1 | |
| PROD_REDIS_PORT=6379 | |
| PROD_REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }} | |
| EOF | |
| - name: AWS SSM Send-Command | |
| uses: peterkimzz/aws-ssm-send-command@master | |
| id: ssm | |
| with: | |
| aws-region: ${{ secrets.AWS_REGION }} | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| instance-ids: "i-084a4704f4e017cc4" | |
| working-directory: / | |
| comment: Deploy | |
| command: | | |
| # EC2 내부에서 prod.env 파일 생성 | |
| cat > /home/ec2-user/prod.env << 'EOF' | |
| SPRING_PROFILES_ACTIVE=prod | |
| CUSTOM_JWT_SECRET_KEY=${{ secrets.JWT_SECRET_KEY }} | |
| CUSTOM_JWT_ACCESS_TOKEN_EXPIRATION_SECONDS=3600 | |
| PROD_DATASOURCE_URL=jdbc:mysql://mysql_1:3306/${{ secrets.DB_NAME }} | |
| PROD_DATASOURCE_USERNAME=${{ secrets.DB_USER }} | |
| PROD_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| PROD_REDIS_HOST=redis_1 | |
| PROD_REDIS_PORT=6379 | |
| PROD_REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }} | |
| EOF | |
| # EC2에서 GHCR 로그인 | |
| echo "${{ secrets.GHCR_PAT }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| # 최신 이미지 pull & 컨테이너 실행 | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }}:latest | |
| docker stop app1 2>/dev/null || true | |
| docker rm app1 2>/dev/null || true | |
| docker run --env-file /home/ec2-user/prod.env -d --name app1 -p 8080:8080 \ | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/${{ env.DOCKER_IMAGE_NAME }}:latest | |
| # dangling image 정리 + .env 삭제 | |
| docker rmi $(docker images -f "dangling=true" -q) | |
| rm -f /home/ec2-user/prod.env |