From d513f875e6dad1f8a766977900bdf7fb1879b52a Mon Sep 17 00:00:00 2001 From: githyj-jang Date: Tue, 19 May 2026 11:02:38 +0900 Subject: [PATCH 1/5] chore: update timezone settings in application configuration and Dockerfile --- Dockerfile | 2 +- src/main/resources/application.yaml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 708b2ef..0631f0f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,4 @@ RUN chown -R app:app /app USER app EXPOSE 19500 -ENTRYPOINT ["java", "-jar", "/app/app.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "/app/app.jar"] \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index ce51114..89e6a53 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,6 +14,9 @@ spring: application: name: reservation-service + jackson: + time-zone: Asia/Seoul + cache: type: redis From f12056ebbc6658772b31d60dc6f4eef6ed5ceb65 Mon Sep 17 00:00:00 2001 From: githyj-jang Date: Tue, 19 May 2026 11:23:35 +0900 Subject: [PATCH 2/5] chore: update environment variables for database and Redis configuration in CI/CD --- .github/workflows/cd-prod.yml | 19 ++++++++++++++++++- .github/workflows/ci-prod.yml | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd-prod.yml b/.github/workflows/cd-prod.yml index c03d60c..c86c912 100644 --- a/.github/workflows/cd-prod.yml +++ b/.github/workflows/cd-prod.yml @@ -73,6 +73,14 @@ jobs: --arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \ --arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \ --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \ + --arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \ + --arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \ + --arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \ + --arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \ + --arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \ + --arg REDIS_HOST "${{ vars.REDIS_HOST }}" \ + --arg REDIS_PORT "${{ vars.REDIS_PORT }}" \ + --arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \ ' .containerDefinitions |= map( if .name == $CN then @@ -81,10 +89,19 @@ jobs: [ {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE}, {"name":"SERVER_PORT","value":$SERVER_PORT}, + {"name":"TZ","value":"Asia/Seoul"}, {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED}, {"name":"EUREKA_HOST","value":$EUREKA_HOST}, {"name":"EUREKA_PORT","value":$EUREKA_PORT}, - {"name":"JWT_SECRET","value":$JWT_SECRET} + {"name":"JWT_SECRET","value":$JWT_SECRET}, + {"name":"POSTGRES_HOST","value":$POSTGRES_HOST}, + {"name":"POSTGRES_PORT","value":$POSTGRES_PORT}, + {"name":"POSTGRES_DB","value":$POSTGRES_DB}, + {"name":"POSTGRES_USER","value":$POSTGRES_USER}, + {"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD}, + {"name":"REDIS_HOST","value":$REDIS_HOST}, + {"name":"REDIS_PORT","value":$REDIS_PORT}, + {"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET} ] + (.environment // []) | unique_by(.name) ) diff --git a/.github/workflows/ci-prod.yml b/.github/workflows/ci-prod.yml index 6feac6f..916cebd 100644 --- a/.github/workflows/ci-prod.yml +++ b/.github/workflows/ci-prod.yml @@ -38,6 +38,7 @@ jobs: run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV # 태그란? 이미지 버전 이름 - name: Configure AWS credentials (OIDC) # OIDC 기반으로 역할 자격증명 + if: github.event_name == 'push' uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} From 3e11601e38c7c712ee8917a1c3feec3b1e5999ad Mon Sep 17 00:00:00 2001 From: githyj-jang Date: Tue, 19 May 2026 12:35:01 +0900 Subject: [PATCH 3/5] chore: update deployment condition to trigger on 'dev' branch --- .github/workflows/cd-prod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd-prod.yml b/.github/workflows/cd-prod.yml index c86c912..43bce88 100644 --- a/.github/workflows/cd-prod.yml +++ b/.github/workflows/cd-prod.yml @@ -25,7 +25,7 @@ env: jobs: deploy: - if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main') }} + if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'dev') }} runs-on: ubuntu-latest timeout-minutes: 20 From 3c9cac85fc7d7f6030801f255bb1df8f6b1c9104 Mon Sep 17 00:00:00 2001 From: githyj-jang Date: Thu, 21 May 2026 00:37:29 +0900 Subject: [PATCH 4/5] refactor: enhance reservation state management with cancellation and modification checks --- .github/workflows/cd-prod.yml | 278 +++++++++--------- .github/workflows/ci-prod.yml | 123 ++++---- .../domain/entity/Reservation.java | 40 +-- .../domain/enums/ReservationTransition.java | 27 -- .../domain/state/ConfirmedState.java | 29 ++ .../domain/state/ReservationState.java | 18 ++ .../enums/ReservationTransitionTest.java | 104 ------- 7 files changed, 262 insertions(+), 357 deletions(-) delete mode 100644 src/main/java/com/michelet/reservation/domain/enums/ReservationTransition.java delete mode 100644 src/test/java/com/michelet/reservation/domain/enums/ReservationTransitionTest.java diff --git a/.github/workflows/cd-prod.yml b/.github/workflows/cd-prod.yml index 43bce88..efc5aed 100644 --- a/.github/workflows/cd-prod.yml +++ b/.github/workflows/cd-prod.yml @@ -1,139 +1,139 @@ -name: CD-PROD - -on: - workflow_run: - workflows: [ "CI-PROD" ] - types: [ completed ] - workflow_dispatch: - -concurrency: - group: cd-prod - cancel-in-progress: false - -permissions: - contents: read - id-token: write - -env: - AWS_REGION: ${{ vars.AWS_REGION }} - ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} - ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }} - ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} - ECS_SERVICE: ${{ vars.ECS_SERVICE }} - TASK_FAMILY: ${{ vars.TASK_FAMILY }} - CONTAINER_NAME: ${{ vars.CONTAINER_NAME }} - -jobs: - deploy: - if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'dev') }} - runs-on: ubuntu-latest - timeout-minutes: 20 - - steps: - - name: Configure AWS credentials (OIDC) - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - - - name: Resolve image tag - id: tag - run: | - if [ "${{ github.event_name }}" = "workflow_run" ]; then - IMAGE_TAG="${{ github.event.workflow_run.head_sha }}" - else - IMAGE_TAG="${{ github.sha }}" - fi - IMAGE_TAG="${IMAGE_TAG:0:7}" - echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" - - - name: Verify image exists in ECR - run: | - aws ecr describe-images \ - --repository-name "${ECR_REPOSITORY}" \ - --image-ids imageTag="${{ steps.tag.outputs.image_tag }}" \ - > /dev/null - - - name: Get current task definition - run: | - aws ecs describe-task-definition \ - --task-definition "${TASK_FAMILY}" \ - --query 'taskDefinition' > taskdef.json - - - name: Render new task definition - env: - IMAGE_URI: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }} - run: | - jq \ - --arg CN "${CONTAINER_NAME}" \ - --arg IMG "${IMAGE_URI}" \ - --arg SPRING_PROFILES_ACTIVE "prod" \ - --arg SERVER_PORT "19500" \ - --arg EUREKA_ENABLED "${{ vars.EUREKA_ENABLED }}" \ - --arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \ - --arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \ - --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \ - --arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \ - --arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \ - --arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \ - --arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \ - --arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \ - --arg REDIS_HOST "${{ vars.REDIS_HOST }}" \ - --arg REDIS_PORT "${{ vars.REDIS_PORT }}" \ - --arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \ - ' - .containerDefinitions |= map( - if .name == $CN then - .image = $IMG - | .environment = ( - [ - {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE}, - {"name":"SERVER_PORT","value":$SERVER_PORT}, - {"name":"TZ","value":"Asia/Seoul"}, - {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED}, - {"name":"EUREKA_HOST","value":$EUREKA_HOST}, - {"name":"EUREKA_PORT","value":$EUREKA_PORT}, - {"name":"JWT_SECRET","value":$JWT_SECRET}, - {"name":"POSTGRES_HOST","value":$POSTGRES_HOST}, - {"name":"POSTGRES_PORT","value":$POSTGRES_PORT}, - {"name":"POSTGRES_DB","value":$POSTGRES_DB}, - {"name":"POSTGRES_USER","value":$POSTGRES_USER}, - {"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD}, - {"name":"REDIS_HOST","value":$REDIS_HOST}, - {"name":"REDIS_PORT","value":$REDIS_PORT}, - {"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET} - ] + (.environment // []) - | unique_by(.name) - ) - else . - end - ) - | del( - .taskDefinitionArn, .revision, .status, - .requiresAttributes, .compatibilities, - .registeredAt, .registeredBy - ) - ' taskdef.json > taskdef.new.json - - - name: Register new task definition revision - id: register - run: | - ARN=$(aws ecs register-task-definition \ - --cli-input-json file://taskdef.new.json \ - --query 'taskDefinition.taskDefinitionArn' \ - --output text) - echo "task_def_arn=${ARN}" >> "$GITHUB_OUTPUT" - - - name: Deploy to ECS service - run: | - aws ecs update-service \ - --cluster "${ECS_CLUSTER}" \ - --service "${ECS_SERVICE}" \ - --task-definition "${{ steps.register.outputs.task_def_arn }}" \ - --force-new-deployment - - - name: Wait for stable - run: | - aws ecs wait services-stable \ - --cluster "${ECS_CLUSTER}" \ - --services "${ECS_SERVICE}" +#name: CD-PROD +# +#on: +# workflow_run: +# workflows: [ "CI-PROD" ] +# types: [ completed ] +# workflow_dispatch: +# +#concurrency: +# group: cd-prod +# cancel-in-progress: false +# +#permissions: +# contents: read +# id-token: write +# +#env: +# AWS_REGION: ${{ vars.AWS_REGION }} +# ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} +# ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }} +# ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} +# ECS_SERVICE: ${{ vars.ECS_SERVICE }} +# TASK_FAMILY: ${{ vars.TASK_FAMILY }} +# CONTAINER_NAME: ${{ vars.CONTAINER_NAME }} +# +#jobs: +# deploy: +# if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main') }} +# runs-on: ubuntu-latest +# timeout-minutes: 20 +# +# steps: +# - name: Configure AWS credentials (OIDC) +# uses: aws-actions/configure-aws-credentials@v4 +# with: +# role-to-assume: ${{ secrets.AWS_ROLE_ARN }} +# aws-region: ${{ env.AWS_REGION }} +# +# - name: Resolve image tag +# id: tag +# run: | +# if [ "${{ github.event_name }}" = "workflow_run" ]; then +# IMAGE_TAG="${{ github.event.workflow_run.head_sha }}" +# else +# IMAGE_TAG="${{ github.sha }}" +# fi +# IMAGE_TAG="${IMAGE_TAG:0:7}" +# echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" +# +# - name: Verify image exists in ECR +# run: | +# aws ecr describe-images \ +# --repository-name "${ECR_REPOSITORY}" \ +# --image-ids imageTag="${{ steps.tag.outputs.image_tag }}" \ +# > /dev/null +# +# - name: Get current task definition +# run: | +# aws ecs describe-task-definition \ +# --task-definition "${TASK_FAMILY}" \ +# --query 'taskDefinition' > taskdef.json +# +# - name: Render new task definition +# env: +# IMAGE_URI: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }} +# run: | +# jq \ +# --arg CN "${CONTAINER_NAME}" \ +# --arg IMG "${IMAGE_URI}" \ +# --arg SPRING_PROFILES_ACTIVE "prod" \ +# --arg SERVER_PORT "19500" \ +# --arg EUREKA_ENABLED "${{ vars.EUREKA_ENABLED }}" \ +# --arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \ +# --arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \ +# --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \ +# --arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \ +# --arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \ +# --arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \ +# --arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \ +# --arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \ +# --arg REDIS_HOST "${{ vars.REDIS_HOST }}" \ +# --arg REDIS_PORT "${{ vars.REDIS_PORT }}" \ +# --arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \ +# ' +# .containerDefinitions |= map( +# if .name == $CN then +# .image = $IMG +# | .environment = ( +# [ +# {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE}, +# {"name":"SERVER_PORT","value":$SERVER_PORT}, +# {"name":"TZ","value":"Asia/Seoul"}, +# {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED}, +# {"name":"EUREKA_HOST","value":$EUREKA_HOST}, +# {"name":"EUREKA_PORT","value":$EUREKA_PORT}, +# {"name":"JWT_SECRET","value":$JWT_SECRET}, +# {"name":"POSTGRES_HOST","value":$POSTGRES_HOST}, +# {"name":"POSTGRES_PORT","value":$POSTGRES_PORT}, +# {"name":"POSTGRES_DB","value":$POSTGRES_DB}, +# {"name":"POSTGRES_USER","value":$POSTGRES_USER}, +# {"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD}, +# {"name":"REDIS_HOST","value":$REDIS_HOST}, +# {"name":"REDIS_PORT","value":$REDIS_PORT}, +# {"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET} +# ] + (.environment // []) +# | unique_by(.name) +# ) +# else . +# end +# ) +# | del( +# .taskDefinitionArn, .revision, .status, +# .requiresAttributes, .compatibilities, +# .registeredAt, .registeredBy +# ) +# ' taskdef.json > taskdef.new.json +# +# - name: Register new task definition revision +# id: register +# run: | +# ARN=$(aws ecs register-task-definition \ +# --cli-input-json file://taskdef.new.json \ +# --query 'taskDefinition.taskDefinitionArn' \ +# --output text) +# echo "task_def_arn=${ARN}" >> "$GITHUB_OUTPUT" +# +# - name: Deploy to ECS service +# run: | +# aws ecs update-service \ +# --cluster "${ECS_CLUSTER}" \ +# --service "${ECS_SERVICE}" \ +# --task-definition "${{ steps.register.outputs.task_def_arn }}" \ +# --force-new-deployment +# +# - name: Wait for stable +# run: | +# aws ecs wait services-stable \ +# --cluster "${ECS_CLUSTER}" \ +# --services "${ECS_SERVICE}" diff --git a/.github/workflows/ci-prod.yml b/.github/workflows/ci-prod.yml index 916cebd..eb0f746 100644 --- a/.github/workflows/ci-prod.yml +++ b/.github/workflows/ci-prod.yml @@ -1,59 +1,64 @@ -name: CI-PROD - -on: - pull_request: - branches: [ main, dev ] # main 브랜치에 pr 했을때 트리거 - push: - branches: [ main, dev ] # main 브랜치에 push 했을때 트리거 - -permissions: - contents: read # 내 코드를 읽을 수 있게(코드이동) 접근 권한을 준다. - id-token: write # OIDC 토큰을 발급할 수 있게 한다. - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout # 내 코드 이동 깃허브 컴퓨터로 - uses: actions/checkout@v4 - - - name: Set up JDK 17 # jdk 환경 설치 - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: "17" - - - name: Set up Gradle => gradle 설치 - uses: gradle/actions/setup-gradle@v4 - - - name: Grant execute permission for gradlew # 실행 권한 설정 - run: chmod +x ./gradlew - - - name: Run tests # 테스트 실행 - run: ./gradlew test --no-daemon -Dspring.profiles.active=test - - - name: Generate image tag # pr이 아닌 push 일때 이미지 태그 생성 - if: github.event_name == 'push' # 커밋 기반으로 어떤 코드 버전이 배포됐는지 정확히 추적 가능 - run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV # 태그란? 이미지 버전 이름 - - - name: Configure AWS credentials (OIDC) # OIDC 기반으로 역할 자격증명 - if: github.event_name == 'push' - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN }} - aws-region: ${{ vars.AWS_REGION }} - - - name: Login to Amazon ECR # 위에서 자격증명하고 ecr 로그인 - if: github.event_name == 'push' - uses: aws-actions/amazon-ecr-login@v2 - - - name: Build Docker image # 이미지 빌드해서 - if: github.event_name == 'push' - run: | - docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} . - - - name: Push Docker image to ECR # 이미지 ecr로 업로드 - if: github.event_name == 'push' - run: | - docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} +#name: CI-PROD +# +#on: +# pull_request: +# branches: [ main ] +# push: +# branches: [ main ] +# +#jobs: +# test: +# runs-on: ubuntu-latest +# permissions: +# contents: read +# +# steps: +# - name: Checkout +# uses: actions/checkout@v4 +# +# - name: Set up JDK 17 +# uses: actions/setup-java@v4 +# with: +# distribution: temurin +# java-version: "17" +# +# - name: Set up Gradle +# uses: gradle/actions/setup-gradle@v4 +# +# - name: Grant execute permission for gradlew +# run: chmod +x ./gradlew +# +# - name: Run tests +# run: ./gradlew test --no-daemon -Dspring.profiles.active=test +# +# build-and-push: +# if: github.event_name == 'push' +# needs: test +# runs-on: ubuntu-latest +# permissions: +# contents: read +# id-token: write +# +# steps: +# - name: Checkout +# uses: actions/checkout@v4 +# +# - name: Generate image tag +# run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV +# +# - name: Configure AWS credentials (OIDC) +# uses: aws-actions/configure-aws-credentials@v4 +# with: +# role-to-assume: ${{ secrets.AWS_ROLE_ARN }} +# aws-region: ${{ vars.AWS_REGION }} +# +# - name: Login to Amazon ECR +# uses: aws-actions/amazon-ecr-login@v2 +# +# - name: Build Docker image +# run: | +# docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} . +# +# - name: Push Docker image to ECR +# run: | +# docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} diff --git a/src/main/java/com/michelet/reservation/domain/entity/Reservation.java b/src/main/java/com/michelet/reservation/domain/entity/Reservation.java index b4514c1..12ed581 100644 --- a/src/main/java/com/michelet/reservation/domain/entity/Reservation.java +++ b/src/main/java/com/michelet/reservation/domain/entity/Reservation.java @@ -113,18 +113,9 @@ public void complete(LocalDateTime now) { if (this.status == ReservationStatus.COMPLETED) { return; // 이미 완료된 경우 멱등 처리 } - if (state.status() != ReservationStatus.CONFIRMED) { - throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION); - } - LocalDateTime windowStart = noshowDeadline.minusMinutes(60); // noshowDeadline - 60min (= slotStart - 30min) - if (now.isBefore(windowStart)) { - throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_EARLY); - } - if (now.isAfter(noshowDeadline)) { - throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_LATE); - } - this.status = state.complete(); - this.state = ReservationStateFactory.from(this.status); + state.assertCompletable(now, noshowDeadline); + this.status = state.complete(); + this.state = ReservationStateFactory.from(this.status); this.checkedInAt = now; } @@ -138,13 +129,11 @@ public boolean requiresSlotReturn() { } public boolean isCancellable() { - return state.status() == ReservationStatus.CONFIRMED - && !LocalDate.now().isAfter(cancelDeadline); + return state.isCancellable(cancelDeadline); } public boolean isModifiable() { - return state.status() == ReservationStatus.CONFIRMED - && !LocalDate.now().isAfter(modifyDeadline); + return state.isModifiable(modifyDeadline); } public boolean requiresRefund() { @@ -157,18 +146,13 @@ public void modify( GuestCount newGuestCount, LocalDateTime newNoshowDeadline ) { - if (state.status() != ReservationStatus.CONFIRMED) { - throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION); - } - if (LocalDate.now().isAfter(modifyDeadline)) { - throw new BusinessException(ReservationErrorCode.MODIFY_DEADLINE_EXCEEDED); - } - this.timeSlotId = newTimeSlotId; - this.reservedDate = newReservedDate; - this.guestCount = newGuestCount; - this.cancelDeadline = newReservedDate.minusDays(2); - this.modifyDeadline = newReservedDate.minusDays(2); - this.noshowDeadline = newNoshowDeadline; + state.assertModifiable(modifyDeadline); + this.timeSlotId = newTimeSlotId; + this.reservedDate = newReservedDate; + this.guestCount = newGuestCount; + this.cancelDeadline = newReservedDate.minusDays(2); + this.modifyDeadline = newReservedDate.minusDays(2); + this.noshowDeadline = newNoshowDeadline; } private static void validateCreateInput( diff --git a/src/main/java/com/michelet/reservation/domain/enums/ReservationTransition.java b/src/main/java/com/michelet/reservation/domain/enums/ReservationTransition.java deleted file mode 100644 index 9451783..0000000 --- a/src/main/java/com/michelet/reservation/domain/enums/ReservationTransition.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.michelet.reservation.domain.enums; - -import java.util.Arrays; - -public enum ReservationTransition { - - WAITING_TO_CONFIRMED (ReservationStatus.WAITING, ReservationStatus.CONFIRMED), - WAITING_TO_CANCELLED_UNPAID (ReservationStatus.WAITING, ReservationStatus.CANCELLED_UNPAID), - CONFIRMED_TO_CANCELLED_PAID (ReservationStatus.CONFIRMED, ReservationStatus.CANCELLED_PAID), - CONFIRMED_TO_COMPLETED (ReservationStatus.CONFIRMED, ReservationStatus.COMPLETED), - CONFIRMED_TO_NO_SHOW (ReservationStatus.CONFIRMED, ReservationStatus.NO_SHOW); - - private final ReservationStatus from; - private final ReservationStatus to; - - ReservationTransition(ReservationStatus from, ReservationStatus to) { - this.from = from; - this.to = to; - } - - public ReservationStatus from() { return from; } - public ReservationStatus to() { return to; } - - public static boolean isAllowed(ReservationStatus from, ReservationStatus to) { - return Arrays.stream(values()).anyMatch(t -> t.from == from && t.to == to); - } -} diff --git a/src/main/java/com/michelet/reservation/domain/state/ConfirmedState.java b/src/main/java/com/michelet/reservation/domain/state/ConfirmedState.java index 7f7783b..5274311 100644 --- a/src/main/java/com/michelet/reservation/domain/state/ConfirmedState.java +++ b/src/main/java/com/michelet/reservation/domain/state/ConfirmedState.java @@ -4,6 +4,7 @@ import com.michelet.reservation.domain.enums.ReservationStatus; import com.michelet.reservation.domain.exception.ReservationErrorCode; import java.time.LocalDate; +import java.time.LocalDateTime; public class ConfirmedState implements ReservationState { @@ -25,6 +26,34 @@ public ReservationStatus markNoShow() { return ReservationStatus.NO_SHOW; } + @Override + public boolean isCancellable(LocalDate cancelDeadline) { + return !LocalDate.now().isAfter(cancelDeadline); + } + + @Override + public boolean isModifiable(LocalDate modifyDeadline) { + return !LocalDate.now().isAfter(modifyDeadline); + } + + @Override + public void assertModifiable(LocalDate modifyDeadline) { + if (LocalDate.now().isAfter(modifyDeadline)) { + throw new BusinessException(ReservationErrorCode.MODIFY_DEADLINE_EXCEEDED); + } + } + + @Override + public void assertCompletable(LocalDateTime now, LocalDateTime noshowDeadline) { + LocalDateTime windowStart = noshowDeadline.minusMinutes(60); + if (now.isBefore(windowStart)) { + throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_EARLY); + } + if (now.isAfter(noshowDeadline)) { + throw new BusinessException(ReservationErrorCode.CHECK_IN_TOO_LATE); + } + } + @Override public boolean requiresSlotReturn() { return true; } diff --git a/src/main/java/com/michelet/reservation/domain/state/ReservationState.java b/src/main/java/com/michelet/reservation/domain/state/ReservationState.java index f1e386c..6b1f97d 100644 --- a/src/main/java/com/michelet/reservation/domain/state/ReservationState.java +++ b/src/main/java/com/michelet/reservation/domain/state/ReservationState.java @@ -4,6 +4,7 @@ import com.michelet.reservation.domain.enums.ReservationStatus; import com.michelet.reservation.domain.exception.ReservationErrorCode; import java.time.LocalDate; +import java.time.LocalDateTime; public interface ReservationState { @@ -28,6 +29,23 @@ default ReservationStatus markNoShow() { throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION); } + // 조건 판단 메서드 — CONFIRMED 외 상태는 false / 예외 + default boolean isCancellable(LocalDate cancelDeadline) { + return false; + } + + default boolean isModifiable(LocalDate modifyDeadline) { + return false; + } + + default void assertModifiable(LocalDate modifyDeadline) { + throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION); + } + + default void assertCompletable(LocalDateTime now, LocalDateTime noshowDeadline) { + throw new BusinessException(ReservationErrorCode.INVALID_STATUS_TRANSITION); + } + // 정책 메서드 — 상태별 구현 필수 boolean requiresSlotReturn(); boolean requiresRefund(); diff --git a/src/test/java/com/michelet/reservation/domain/enums/ReservationTransitionTest.java b/src/test/java/com/michelet/reservation/domain/enums/ReservationTransitionTest.java deleted file mode 100644 index f855fe7..0000000 --- a/src/test/java/com/michelet/reservation/domain/enums/ReservationTransitionTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.michelet.reservation.domain.enums; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -class ReservationTransitionTest { - - @Nested - class AllowedTransitions { - - @Test - void WAITING에서_CONFIRMED로_전이_허용() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.WAITING, ReservationStatus.CONFIRMED)).isTrue(); - } - - @Test - void WAITING에서_CANCELLED_UNPAID로_전이_허용() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.WAITING, ReservationStatus.CANCELLED_UNPAID)).isTrue(); - } - - @Test - void CONFIRMED에서_CANCELLED_PAID로_전이_허용() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CONFIRMED, ReservationStatus.CANCELLED_PAID)).isTrue(); - } - - @Test - void CONFIRMED에서_COMPLETED로_전이_허용() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CONFIRMED, ReservationStatus.COMPLETED)).isTrue(); - } - - @Test - void CONFIRMED에서_NO_SHOW로_전이_허용() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CONFIRMED, ReservationStatus.NO_SHOW)).isTrue(); - } - } - - @Nested - class ForbiddenTransitions { - - @Test - void CONFIRMED에서_WAITING으로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CONFIRMED, ReservationStatus.WAITING)).isFalse(); - } - - @Test - void CONFIRMED에서_CONFIRMED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CONFIRMED, ReservationStatus.CONFIRMED)).isFalse(); - } - - @Test - void COMPLETED에서_CONFIRMED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.COMPLETED, ReservationStatus.CONFIRMED)).isFalse(); - } - - @Test - void NO_SHOW에서_CONFIRMED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.NO_SHOW, ReservationStatus.CONFIRMED)).isFalse(); - } - - @Test - void CANCELLED_PAID에서_CONFIRMED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CANCELLED_PAID, ReservationStatus.CONFIRMED)).isFalse(); - } - - @Test - void CANCELLED_UNPAID에서_CONFIRMED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.CANCELLED_UNPAID, ReservationStatus.CONFIRMED)).isFalse(); - } - - @Test - void WAITING에서_CANCELLED_PAID로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.WAITING, ReservationStatus.CANCELLED_PAID)).isFalse(); - } - - @Test - void WAITING에서_COMPLETED로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.WAITING, ReservationStatus.COMPLETED)).isFalse(); - } - - @Test - void WAITING에서_NO_SHOW로_전이_금지() { - assertThat(ReservationTransition.isAllowed(ReservationStatus.WAITING, ReservationStatus.NO_SHOW)).isFalse(); - } - } - - @Nested - class TransitionCount { - - @Test - void 허용_전이는_정확히_5개다() { - long count = 0; - for (ReservationStatus from : ReservationStatus.values()) { - for (ReservationStatus to : ReservationStatus.values()) { - if (ReservationTransition.isAllowed(from, to)) { - count++; - } - } - } - assertThat(count).isEqualTo(5); - } - } -} From b347dda6f8a11911f3a1c33d644dbf528c837f34 Mon Sep 17 00:00:00 2001 From: githyj-jang Date: Thu, 21 May 2026 00:40:06 +0900 Subject: [PATCH 5/5] refactor: update CI/CD workflows for improved deployment and testing processes --- .github/workflows/cd-prod.yml | 278 +++++++++++++++++----------------- .github/workflows/ci-prod.yml | 128 ++++++++-------- 2 files changed, 203 insertions(+), 203 deletions(-) diff --git a/.github/workflows/cd-prod.yml b/.github/workflows/cd-prod.yml index efc5aed..c86c912 100644 --- a/.github/workflows/cd-prod.yml +++ b/.github/workflows/cd-prod.yml @@ -1,139 +1,139 @@ -#name: CD-PROD -# -#on: -# workflow_run: -# workflows: [ "CI-PROD" ] -# types: [ completed ] -# workflow_dispatch: -# -#concurrency: -# group: cd-prod -# cancel-in-progress: false -# -#permissions: -# contents: read -# id-token: write -# -#env: -# AWS_REGION: ${{ vars.AWS_REGION }} -# ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} -# ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }} -# ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} -# ECS_SERVICE: ${{ vars.ECS_SERVICE }} -# TASK_FAMILY: ${{ vars.TASK_FAMILY }} -# CONTAINER_NAME: ${{ vars.CONTAINER_NAME }} -# -#jobs: -# deploy: -# if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main') }} -# runs-on: ubuntu-latest -# timeout-minutes: 20 -# -# steps: -# - name: Configure AWS credentials (OIDC) -# uses: aws-actions/configure-aws-credentials@v4 -# with: -# role-to-assume: ${{ secrets.AWS_ROLE_ARN }} -# aws-region: ${{ env.AWS_REGION }} -# -# - name: Resolve image tag -# id: tag -# run: | -# if [ "${{ github.event_name }}" = "workflow_run" ]; then -# IMAGE_TAG="${{ github.event.workflow_run.head_sha }}" -# else -# IMAGE_TAG="${{ github.sha }}" -# fi -# IMAGE_TAG="${IMAGE_TAG:0:7}" -# echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" -# -# - name: Verify image exists in ECR -# run: | -# aws ecr describe-images \ -# --repository-name "${ECR_REPOSITORY}" \ -# --image-ids imageTag="${{ steps.tag.outputs.image_tag }}" \ -# > /dev/null -# -# - name: Get current task definition -# run: | -# aws ecs describe-task-definition \ -# --task-definition "${TASK_FAMILY}" \ -# --query 'taskDefinition' > taskdef.json -# -# - name: Render new task definition -# env: -# IMAGE_URI: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }} -# run: | -# jq \ -# --arg CN "${CONTAINER_NAME}" \ -# --arg IMG "${IMAGE_URI}" \ -# --arg SPRING_PROFILES_ACTIVE "prod" \ -# --arg SERVER_PORT "19500" \ -# --arg EUREKA_ENABLED "${{ vars.EUREKA_ENABLED }}" \ -# --arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \ -# --arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \ -# --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \ -# --arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \ -# --arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \ -# --arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \ -# --arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \ -# --arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \ -# --arg REDIS_HOST "${{ vars.REDIS_HOST }}" \ -# --arg REDIS_PORT "${{ vars.REDIS_PORT }}" \ -# --arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \ -# ' -# .containerDefinitions |= map( -# if .name == $CN then -# .image = $IMG -# | .environment = ( -# [ -# {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE}, -# {"name":"SERVER_PORT","value":$SERVER_PORT}, -# {"name":"TZ","value":"Asia/Seoul"}, -# {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED}, -# {"name":"EUREKA_HOST","value":$EUREKA_HOST}, -# {"name":"EUREKA_PORT","value":$EUREKA_PORT}, -# {"name":"JWT_SECRET","value":$JWT_SECRET}, -# {"name":"POSTGRES_HOST","value":$POSTGRES_HOST}, -# {"name":"POSTGRES_PORT","value":$POSTGRES_PORT}, -# {"name":"POSTGRES_DB","value":$POSTGRES_DB}, -# {"name":"POSTGRES_USER","value":$POSTGRES_USER}, -# {"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD}, -# {"name":"REDIS_HOST","value":$REDIS_HOST}, -# {"name":"REDIS_PORT","value":$REDIS_PORT}, -# {"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET} -# ] + (.environment // []) -# | unique_by(.name) -# ) -# else . -# end -# ) -# | del( -# .taskDefinitionArn, .revision, .status, -# .requiresAttributes, .compatibilities, -# .registeredAt, .registeredBy -# ) -# ' taskdef.json > taskdef.new.json -# -# - name: Register new task definition revision -# id: register -# run: | -# ARN=$(aws ecs register-task-definition \ -# --cli-input-json file://taskdef.new.json \ -# --query 'taskDefinition.taskDefinitionArn' \ -# --output text) -# echo "task_def_arn=${ARN}" >> "$GITHUB_OUTPUT" -# -# - name: Deploy to ECS service -# run: | -# aws ecs update-service \ -# --cluster "${ECS_CLUSTER}" \ -# --service "${ECS_SERVICE}" \ -# --task-definition "${{ steps.register.outputs.task_def_arn }}" \ -# --force-new-deployment -# -# - name: Wait for stable -# run: | -# aws ecs wait services-stable \ -# --cluster "${ECS_CLUSTER}" \ -# --services "${ECS_SERVICE}" +name: CD-PROD + +on: + workflow_run: + workflows: [ "CI-PROD" ] + types: [ completed ] + workflow_dispatch: + +concurrency: + group: cd-prod + cancel-in-progress: false + +permissions: + contents: read + id-token: write + +env: + AWS_REGION: ${{ vars.AWS_REGION }} + ECR_REGISTRY: ${{ vars.ECR_REGISTRY }} + ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }} + ECS_CLUSTER: ${{ vars.ECS_CLUSTER }} + ECS_SERVICE: ${{ vars.ECS_SERVICE }} + TASK_FAMILY: ${{ vars.TASK_FAMILY }} + CONTAINER_NAME: ${{ vars.CONTAINER_NAME }} + +jobs: + deploy: + if: ${{ github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main') }} + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Resolve image tag + id: tag + run: | + if [ "${{ github.event_name }}" = "workflow_run" ]; then + IMAGE_TAG="${{ github.event.workflow_run.head_sha }}" + else + IMAGE_TAG="${{ github.sha }}" + fi + IMAGE_TAG="${IMAGE_TAG:0:7}" + echo "image_tag=${IMAGE_TAG}" >> "$GITHUB_OUTPUT" + + - name: Verify image exists in ECR + run: | + aws ecr describe-images \ + --repository-name "${ECR_REPOSITORY}" \ + --image-ids imageTag="${{ steps.tag.outputs.image_tag }}" \ + > /dev/null + + - name: Get current task definition + run: | + aws ecs describe-task-definition \ + --task-definition "${TASK_FAMILY}" \ + --query 'taskDefinition' > taskdef.json + + - name: Render new task definition + env: + IMAGE_URI: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ steps.tag.outputs.image_tag }} + run: | + jq \ + --arg CN "${CONTAINER_NAME}" \ + --arg IMG "${IMAGE_URI}" \ + --arg SPRING_PROFILES_ACTIVE "prod" \ + --arg SERVER_PORT "19500" \ + --arg EUREKA_ENABLED "${{ vars.EUREKA_ENABLED }}" \ + --arg EUREKA_HOST "${{ vars.EUREKA_HOST }}" \ + --arg EUREKA_PORT "${{ vars.EUREKA_PORT }}" \ + --arg JWT_SECRET "${{ secrets.JWT_SECRET }}" \ + --arg POSTGRES_HOST "${{ vars.POSTGRES_HOST }}" \ + --arg POSTGRES_PORT "${{ vars.POSTGRES_PORT }}" \ + --arg POSTGRES_DB "${{ vars.POSTGRES_DB }}" \ + --arg POSTGRES_USER "${{ vars.POSTGRES_USER }}" \ + --arg POSTGRES_PASSWORD "${{ secrets.POSTGRES_PASSWORD }}" \ + --arg REDIS_HOST "${{ vars.REDIS_HOST }}" \ + --arg REDIS_PORT "${{ vars.REDIS_PORT }}" \ + --arg INTERNAL_AUTH_SECRET "${{ secrets.INTERNAL_AUTH_SECRET }}" \ + ' + .containerDefinitions |= map( + if .name == $CN then + .image = $IMG + | .environment = ( + [ + {"name":"SPRING_PROFILES_ACTIVE","value":$SPRING_PROFILES_ACTIVE}, + {"name":"SERVER_PORT","value":$SERVER_PORT}, + {"name":"TZ","value":"Asia/Seoul"}, + {"name":"EUREKA_ENABLED","value":$EUREKA_ENABLED}, + {"name":"EUREKA_HOST","value":$EUREKA_HOST}, + {"name":"EUREKA_PORT","value":$EUREKA_PORT}, + {"name":"JWT_SECRET","value":$JWT_SECRET}, + {"name":"POSTGRES_HOST","value":$POSTGRES_HOST}, + {"name":"POSTGRES_PORT","value":$POSTGRES_PORT}, + {"name":"POSTGRES_DB","value":$POSTGRES_DB}, + {"name":"POSTGRES_USER","value":$POSTGRES_USER}, + {"name":"POSTGRES_PASSWORD","value":$POSTGRES_PASSWORD}, + {"name":"REDIS_HOST","value":$REDIS_HOST}, + {"name":"REDIS_PORT","value":$REDIS_PORT}, + {"name":"INTERNAL_AUTH_SECRET","value":$INTERNAL_AUTH_SECRET} + ] + (.environment // []) + | unique_by(.name) + ) + else . + end + ) + | del( + .taskDefinitionArn, .revision, .status, + .requiresAttributes, .compatibilities, + .registeredAt, .registeredBy + ) + ' taskdef.json > taskdef.new.json + + - name: Register new task definition revision + id: register + run: | + ARN=$(aws ecs register-task-definition \ + --cli-input-json file://taskdef.new.json \ + --query 'taskDefinition.taskDefinitionArn' \ + --output text) + echo "task_def_arn=${ARN}" >> "$GITHUB_OUTPUT" + + - name: Deploy to ECS service + run: | + aws ecs update-service \ + --cluster "${ECS_CLUSTER}" \ + --service "${ECS_SERVICE}" \ + --task-definition "${{ steps.register.outputs.task_def_arn }}" \ + --force-new-deployment + + - name: Wait for stable + run: | + aws ecs wait services-stable \ + --cluster "${ECS_CLUSTER}" \ + --services "${ECS_SERVICE}" diff --git a/.github/workflows/ci-prod.yml b/.github/workflows/ci-prod.yml index eb0f746..6743096 100644 --- a/.github/workflows/ci-prod.yml +++ b/.github/workflows/ci-prod.yml @@ -1,64 +1,64 @@ -#name: CI-PROD -# -#on: -# pull_request: -# branches: [ main ] -# push: -# branches: [ main ] -# -#jobs: -# test: -# runs-on: ubuntu-latest -# permissions: -# contents: read -# -# steps: -# - name: Checkout -# uses: actions/checkout@v4 -# -# - name: Set up JDK 17 -# uses: actions/setup-java@v4 -# with: -# distribution: temurin -# java-version: "17" -# -# - name: Set up Gradle -# uses: gradle/actions/setup-gradle@v4 -# -# - name: Grant execute permission for gradlew -# run: chmod +x ./gradlew -# -# - name: Run tests -# run: ./gradlew test --no-daemon -Dspring.profiles.active=test -# -# build-and-push: -# if: github.event_name == 'push' -# needs: test -# runs-on: ubuntu-latest -# permissions: -# contents: read -# id-token: write -# -# steps: -# - name: Checkout -# uses: actions/checkout@v4 -# -# - name: Generate image tag -# run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV -# -# - name: Configure AWS credentials (OIDC) -# uses: aws-actions/configure-aws-credentials@v4 -# with: -# role-to-assume: ${{ secrets.AWS_ROLE_ARN }} -# aws-region: ${{ vars.AWS_REGION }} -# -# - name: Login to Amazon ECR -# uses: aws-actions/amazon-ecr-login@v2 -# -# - name: Build Docker image -# run: | -# docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} . -# -# - name: Push Docker image to ECR -# run: | -# docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} +name: CI-PROD + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Run tests + run: ./gradlew test --no-daemon -Dspring.profiles.active=test + + build-and-push: + if: github.event_name == 'push' + needs: test + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Generate image tag + run: echo "IMAGE_TAG=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ vars.AWS_REGION }} + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + + - name: Build Docker image + run: | + docker build -t ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} . + + - name: Push Docker image to ECR + run: | + docker push ${{ vars.ECR_REGISTRY }}/${{ vars.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}