From 453eff1bce2865d4789617e0e0a4f17a8ce5fb5f Mon Sep 17 00:00:00 2001 From: tlarbals824 Date: Tue, 5 May 2026 15:47:26 +0900 Subject: [PATCH 1/3] Ensure release publishing checks pushed changes Use the push event before/after range so API client publishing is gated by the changes introduced by the main push, not by the current develop/main branch delta.\n\nConstraint: dorny/paths-filter defaults compared develop...main on release pushes, missing src changes already present in develop.\nRejected: Always publish API client | would publish even for docs-only releases.\nConfidence: high\nScope-risk: narrow\nDirective: Keep release path filtering anchored to push event SHAs for main push workflows.\nTested: git diff --check\nNot-tested: GitHub Actions runtime execution --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a5fa7dd..56dbff7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,8 @@ jobs: - uses: dorny/paths-filter@v3 id: filter with: + base: ${{ github.event.before }} + ref: ${{ github.sha }} filters: | src: - 'src/**' From 9cf2d2cce6d896205aa07c1e9d70fe360030f0f7 Mon Sep 17 00:00:00 2001 From: tlarbals824 Date: Tue, 5 May 2026 15:55:45 +0900 Subject: [PATCH 2/3] Avoid apt curl install in runtime image Switch the runtime stage to the Alpine Temurin JRE so the healthcheck can use BusyBox wget already present in the image. This removes apt package downloads from Docker builds.\n\nConstraint: GitHub Actions image builds were spending minutes in apt-get installing curl for healthchecks.\nRejected: Keep Ubuntu Temurin and install curl | preserves the slow apt network path.\nConfidence: high\nScope-risk: narrow\nDirective: Keep container healthchecks dependency-free from apt packages.\nTested: docker manifest inspect eclipse-temurin:25-jre-alpine; git diff --check\nNot-tested: Full docker build --- Dockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 28d1337..b37ac10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,14 +13,12 @@ COPY src ./src RUN gradle bootJar --no-daemon # ===== 실행 스테이지 ===== -FROM eclipse-temurin:25-jre +FROM eclipse-temurin:25-jre-alpine WORKDIR /app COPY --from=build /app/build/libs/*.jar app.jar -RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* - HEALTHCHECK --interval=10s --timeout=5s --start-period=90s --retries=18 \ - CMD curl -f http://localhost:8081/actuator/health/readiness || exit 1 + CMD wget -q -O /dev/null http://127.0.0.1:8081/actuator/health/readiness || exit 1 EXPOSE 8080 -ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar"] From 22620747401a93694d2a72164159dcc26f102ffb Mon Sep 17 00:00:00 2001 From: tlarbals824 Date: Tue, 5 May 2026 16:05:18 +0900 Subject: [PATCH 3/3] Build jars outside runtime images with Gradle cache Move release Docker publishing to a jar-only runtime image and use Gradle's CI cache for the application build. This keeps Docker focused on packaging and lets setup-gradle own dependency and build-cache reuse across CI jobs.\n\nConstraint: Docker-internal Gradle builds could not directly reuse the host Gradle cache used by CI.\nRejected: BuildKit-only Gradle cache inside Docker | improves the old shape but still couples compilation to image construction.\nConfidence: high\nScope-risk: moderate\nDirective: Keep release image builds jar-only unless runtime packaging needs source-aware image generation.\nTested: ./gradlew build --build-cache --no-daemon; docker build --no-cache -t ject-server:jar-only-runtime-test .; container app.jar/wget BusyBox smoke; git diff --check\nNot-tested: actionlint unavailable locally; GitHub Actions cache round-trip --- .dockerignore | 5 +++++ .github/workflows/ci-pr.yml | 29 +++----------------------- .github/workflows/ci-push.yml | 15 +------------- .github/workflows/release.yml | 38 ++++++++++++++++++++++++++--------- Dockerfile | 18 ++++------------- gradle.properties | 1 + 6 files changed, 42 insertions(+), 64 deletions(-) create mode 100644 .dockerignore create mode 100644 gradle.properties diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..dbb52f8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +* +!Dockerfile +!build/ +!build/libs/ +!build/libs/app.jar diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 33b59e2..478f828 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -37,21 +37,11 @@ jobs: java-version: '25' distribution: 'temurin' - - name: Gradle 의존성 캐싱 - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: 프로젝트 빌드 - run: ./gradlew build + run: ./gradlew build --build-cache - name: 빌드 결과 댓글 if: always() @@ -90,24 +80,11 @@ jobs: java-version: '25' distribution: 'temurin' - - name: Gradle 의존성 캐싱 - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: 프로젝트 빌드 - env: - GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH_CLIENT_ID }} - GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }} - run: ./gradlew build + run: ./gradlew build --build-cache - name: 빌드 결과 댓글 if: always() @@ -137,4 +114,4 @@ jobs: issue_number: context.payload.pull_request.number, name: 'ready-to-build' }); - } catch (e) {} \ No newline at end of file + } catch (e) {} diff --git a/.github/workflows/ci-push.yml b/.github/workflows/ci-push.yml index db246c3..36b5b9f 100644 --- a/.github/workflows/ci-push.yml +++ b/.github/workflows/ci-push.yml @@ -20,21 +20,8 @@ jobs: java-version: '25' distribution: 'temurin' - - name: Gradle 의존성 캐싱 - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: 프로젝트 빌드 - env: - GOOGLE_OAUTH_CLIENT_ID: ${{ secrets.GOOGLE_OAUTH_CLIENT_ID }} - GOOGLE_OAUTH_CLIENT_SECRET: ${{ secrets.GOOGLE_OAUTH_CLIENT_SECRET }} - run: ./gradlew build \ No newline at end of file + run: ./gradlew build --build-cache diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56dbff7..ebbba35 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,6 +64,22 @@ jobs: - name: Repository 접근 uses: actions/checkout@v4 + - name: JDK 25 셋팅 + uses: actions/setup-java@v4 + with: + java-version: '25' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: 애플리케이션 JAR 빌드 + run: | + ./gradlew bootJar --no-daemon --build-cache + BOOT_JAR=$(find build/libs -maxdepth 1 -type f -name '*.jar' ! -name '*-plain.jar' | head -n 1) + test -n "$BOOT_JAR" + cp "$BOOT_JAR" build/libs/app.jar + - name: AWS 자격증명 설정 uses: aws-actions/configure-aws-credentials@v4 with: @@ -74,17 +90,19 @@ jobs: id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - - name: Docker 이미지 빌드 & 푸시 - env: - REGISTRY: ${{ steps.login-ecr.outputs.registry }} - run: | - IMAGE=$REGISTRY/backend - TAG=v${{ needs.calculate-version.outputs.version }} + - name: Docker Buildx 설정 + uses: docker/setup-buildx-action@v3 - docker build -t $IMAGE:$TAG . - docker tag $IMAGE:$TAG $IMAGE:latest - docker push $IMAGE:$TAG - docker push $IMAGE:latest + - name: Docker 이미지 빌드 & 푸시 + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ steps.login-ecr.outputs.registry }}/backend:v${{ needs.calculate-version.outputs.version }} + ${{ steps.login-ecr.outputs.registry }}/backend:latest + cache-from: type=gha,scope=backend-runtime + cache-to: type=gha,scope=backend-runtime,mode=max - name: 서버 배포 uses: appleboy/ssh-action@v1.0.3 diff --git a/Dockerfile b/Dockerfile index b37ac10..b8f2733 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,11 @@ -# ===== 빌드 스테이지 ===== -FROM gradle:jdk25 AS build -WORKDIR /app - -# 의존성 먼저 복사 (캐싱 활용) -COPY build.gradle.kts settings.gradle.kts ./ -COPY buildSrc ./buildSrc -COPY gradle ./gradle -RUN gradle dependencies --no-daemon || true - -# 소스 코드 복사 & 빌드 -COPY src ./src -RUN gradle bootJar --no-daemon +# syntax=docker/dockerfile:1.7 # ===== 실행 스테이지 ===== FROM eclipse-temurin:25-jre-alpine WORKDIR /app -COPY --from=build /app/build/libs/*.jar app.jar + +ARG JAR_FILE=build/libs/app.jar +COPY ${JAR_FILE} app.jar HEALTHCHECK --interval=10s --timeout=5s --start-period=90s --retries=18 \ CMD wget -q -O /dev/null http://127.0.0.1:8081/actuator/health/readiness || exit 1 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1608900 --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +org.gradle.caching=true