diff --git a/.drone.yml b/.drone.yml index 3bf0db2..98b84a4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -6,13 +6,6 @@ environment: DOCKER_BUILDKIT: 1 steps: - - name: test-and-jar - image: gradle:8.10.2-jdk21 - # No host volumes; keep cache in the container layer only - commands: - - gradle --version - - gradle --no-daemon clean test bootJar - - name: build-image image: plugins/docker settings: @@ -27,11 +20,19 @@ steps: - ${DRONE_BRANCH/\//-}-${DRONE_COMMIT_SHA:0:7} - latest buildkit: true + # ⬇️ Pull last image and reuse its layers (huge win) + cache_from: + - rubble.se:5000/hemhub/api:latest labels: org.opencontainers.image.source: ${DRONE_GIT_HTTP_URL} org.opencontainers.image.revision: ${DRONE_COMMIT_SHA} org.opencontainers.image.created: ${DRONE_BUILD_FINISHED} org.opencontainers.image.version: ${DRONE_TAG:-${DRONE_COMMIT_SHA:0:7}} + when: + branch: + include: [ main, develop ] + event: + include: [ push, pull_request, tag ] - name: publish-tag image: plugins/docker @@ -46,11 +47,16 @@ steps: tags: - ${DRONE_TAG} buildkit: true + cache_from: + - rubble.se:5000/hemhub/api:latest labels: org.opencontainers.image.source: ${DRONE_GIT_HTTP_URL} org.opencontainers.image.revision: ${DRONE_COMMIT_SHA} org.opencontainers.image.created: ${DRONE_BUILD_FINISHED} org.opencontainers.image.version: ${DRONE_TAG} + when: + event: + include: [ tag ] trigger: event: diff --git a/Dockerfile b/Dockerfile index dc246c3..8b39c7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,50 @@ -# ---- build ---- -FROM gradle:8.10.2-jdk21 AS build +# ---------- Base build image (pins Gradle + JDK) ---------- +FROM gradle:8.10.2-jdk21-alpine AS build-base WORKDIR /workspace -COPY . . -RUN gradle clean bootJar --no-daemon -# ---- runtime ---- -FROM eclipse-temurin:21-jre -ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75" +# Pre-copy only files that affect dependency resolution +COPY settings.gradle.kts settings.gradle.kts +COPY settings.gradle settings.gradle +COPY build.gradle.kts build.gradle.kts +COPY build.gradle build.gradle +COPY gradle 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 --from=build /workspace/build/libs/*-SNAPSHOT.jar app.jar -EXPOSE 8080 + +# 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 -ENTRYPOINT ["sh","-c","java $JAVA_OPTS -jar /app/app.jar"] + +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"]