# ---------- Base build image (pins Gradle + JDK) ---------- FROM gradle:8.10.2-jdk21-alpine AS build-base WORKDIR /workspace # Pre-copy only files that affect dependency resolution COPY settings.gradle settings.gradle COPY build.gradle build.gradle COPY gradlew gradlew # Warm Gradle wrapper & dependency cache (layer-cached in the image) RUN ./gradlew --version || gradle --version RUN ./gradlew --no-daemon --stacktrace dependencies || true # ---------- Test stage (fail fast but reuse the warmed deps) ---------- FROM build-base AS test # Copy the full project now (invalidates later layers only when code changes) COPY . . RUN ./gradlew --no-daemon clean test # ---------- Package stage (reuses compiled classes incrementally) ---------- FROM test AS package RUN ./gradlew --no-daemon bootJar # Find the built jar path RUN ls -lah build/libs # ---------- Runtime image ---------- FROM eclipse-temurin:21-jre-alpine RUN addgroup -S app && adduser -S app -G app USER app WORKDIR /app # Copy the application jar COPY --from=package /workspace/build/libs/*SNAPSHOT*.jar /app/app.jar # Healthcheck via Spring Actuator (optional if you have it) # HEALTHCHECK --interval=30s --timeout=3s --start-period=20s --retries=3 \ # CMD wget -qO- http://localhost:8080/actuator/health/liveness | grep -q '"status":"UP"' || exit 1 # Graceful shutdown ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75 -XX:InitialRAMPercentage=50" ENV SERVER_SHUTDOWN=graceful ENV SPRING_LIFECYCLE_TIMEOUT=20s LABEL com.centurylinklabs.watchtower.enable=true EXPOSE 8080 ENTRYPOINT ["sh","-c","java $JAVA_OPTS -Dserver.shutdown=${SERVER_SHUTDOWN} -Dspring.lifecycle.timeout-per-shutdown-phase=${SPRING_LIFECYCLE_TIMEOUT} -jar /app/app.jar"]