Snapshot of the in-flight state that already existed, identically, in both this worktree and the main checkout before this session began: the initial HTTP Client platform implementation (previously untracked), the redis-lab removal, and the JPA / object-storage / notification integration work. Kept separate from this session's HTTP Client review response, which lands in the following commit, so the two bodies of work stay reviewable apart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
130 lines
6.3 KiB
Docker
130 lines
6.3 KiB
Docker
# syntax=docker/dockerfile:1.7-labs@sha256:b99fecfe00268a8b556fad7d9c37ee25d716ae08a5d7320e6d51c4dd83246894
|
|
# =============================================================================
|
|
# sample-portfolio standalone demo image — twin of src/Dockerfile.
|
|
#
|
|
# Builds and runs the REFERENCE app (SamplePortfolioApplication), not the
|
|
# production composition root (CaSkeletonApplication) that src/Dockerfile builds.
|
|
# The sample is the demo-friendly entrypoint: its application.yml self-provides
|
|
# defaults for every env placeholder, so the only external dependency it needs
|
|
# to boot is a reachable PostgreSQL (datasource + Flyway sample migrations).
|
|
#
|
|
# Build (no build-args required — this is a disposable demo, not a release artifact):
|
|
# docker build -f src/Dockerfile.sample src/ -t ca-sample:local
|
|
#
|
|
# Run (point APP_DATASOURCE_URL at a reachable Postgres; localhost default shown):
|
|
# docker run --rm -p 8080:8080 -p 9001:9001 \
|
|
# -e APP_DATASOURCE_URL=jdbc:postgresql://host.docker.internal:5432/ca_skeleton \
|
|
# ca-sample:local
|
|
#
|
|
# The multi-stage build, --parents descriptor glob, STRICT lock verification,
|
|
# non-root user, read-only-root-fs writable mounts, and JVM container ergonomics
|
|
# are all identical to src/Dockerfile — only the bootJar target differs. Keep the
|
|
# two files' builder stages in sync.
|
|
# =============================================================================
|
|
|
|
# Demo defaults so the image builds with zero build-args. Two format constraints from the root
|
|
# build.gradle configuration guard (feature-build-release-supply-chain-contract D1/D9):
|
|
# - RELEASE_VERSION must be bare MAJOR.MINOR.PATCH — no pre-release/build suffix (build.gradle L21).
|
|
# The "-sample" marker therefore lives only on BUILD_VERSION, which is a label, not a gradle prop.
|
|
# - GIT_SHA must be 7-40 hex chars (build.gradle L35); 0000000 is the placeholder.
|
|
ARG RELEASE_VERSION=0.0.0
|
|
ARG BUILD_VERSION=0.0.0-sample
|
|
ARG GIT_SHA=0000000
|
|
ARG SOURCE_URL=https://example.invalid/ca-tmpl-sample
|
|
|
|
# ---- Stage 1: builder -------------------------------------------------------
|
|
# Uses the full JDK only in the build stage, never in the final image.
|
|
FROM eclipse-temurin:21-jdk-jammy@sha256:801b7e1a9c4befaf82bf9a2a58025ef43a7694bbc84779187ad0524d84742772 AS builder
|
|
|
|
ARG RELEASE_VERSION
|
|
ARG GIT_SHA
|
|
|
|
WORKDIR /build/src
|
|
|
|
# Copy the Gradle wrapper and every module's build descriptor + dependency lockfile FIRST,
|
|
# so the expensive dependency-resolution layer is cached and only re-runs when a build.gradle
|
|
# or gradle.lockfile changes (D8). `--parents` preserves each file's directory structure, so a
|
|
# single structure-preserving glob replaces a per-module COPY list: new modules are picked up
|
|
# automatically and this stage never drifts out of sync with settings.gradle.
|
|
# (Requires the labs Dockerfile frontend — see the `# syntax` directive at the top of this file.)
|
|
COPY gradlew ./
|
|
COPY gradle/ gradle/
|
|
COPY config/ ./config/
|
|
COPY --parents settings.gradle build.gradle **/build.gradle **/gradle.lockfile ./
|
|
|
|
# Resolve every module configuration in STRICT mode (no --write-locks in a demo build either).
|
|
RUN test -n "${RELEASE_VERSION}" \
|
|
&& test -n "${GIT_SHA}" \
|
|
&& ./gradlew verifyDependencyLocks --no-daemon --quiet \
|
|
-PreleaseVersion="${RELEASE_VERSION}" -PgitRevision="${GIT_SHA}"
|
|
|
|
# Copy full source and stage the executable sample JAR at Gradle's declared Docker output path.
|
|
COPY . .
|
|
RUN ./gradlew :sample-portfolio:stageDockerJar --no-daemon -x test \
|
|
-PreleaseVersion="${RELEASE_VERSION}" -PgitRevision="${GIT_SHA}"
|
|
|
|
# ---- Stage 2: runtime image -------------------------------------------------
|
|
# JRE-only slim image (no full JDK in the demo image either).
|
|
FROM eclipse-temurin:21-jre-jammy@sha256:199aebeb3adcde4910695cdebfe782ada38dadb6cc8013159b58d3724451befd AS runtime
|
|
|
|
ARG BUILD_VERSION
|
|
ARG GIT_SHA
|
|
ARG SOURCE_URL
|
|
|
|
# OCI image labels. Unlike the release image (src/Dockerfile), the demo image does NOT hard-fail
|
|
# on missing metadata — the ARG defaults above keep it buildable with no build-args.
|
|
LABEL org.opencontainers.image.title="caskeleton-sample" \
|
|
org.opencontainers.image.description="ca-tmpl sample-portfolio reference/demo application" \
|
|
org.opencontainers.image.source="${SOURCE_URL}" \
|
|
org.opencontainers.image.revision="${GIT_SHA}" \
|
|
org.opencontainers.image.version="${BUILD_VERSION}"
|
|
|
|
# ---- Locale / timezone ------------------------------------------------------
|
|
ENV TZ=UTC \
|
|
LANG=C.UTF-8 \
|
|
LC_ALL=C.UTF-8
|
|
|
|
# ---- Writable HOME under read-only root fs ----------------------------------
|
|
ENV HOME=/tmp
|
|
|
|
# ---- JVM ergonomics ---------------------------------------------------------
|
|
# Identical to src/Dockerfile: container-aware heap, fail-fast on OOM, heap dump to a
|
|
# writable mount, and Tomcat temp redirected to /tmp for a read-only root filesystem.
|
|
ENV JAVA_TOOL_OPTIONS="\
|
|
-XX:MaxRAMPercentage=75 \
|
|
-XX:+UseContainerSupport \
|
|
-XX:+ExitOnOutOfMemoryError \
|
|
-XX:+HeapDumpOnOutOfMemoryError \
|
|
-XX:HeapDumpPath=/var/tmp/heap \
|
|
-Dserver.tomcat.basedir=/tmp"
|
|
|
|
# ---- Filesystem layout (read-only root filesystem) --------------------------
|
|
# At runtime /var/tmp/heap and /tmp MUST be writable mounts (tmpfs/emptyDir).
|
|
RUN mkdir -p /var/tmp/heap && chmod 1777 /var/tmp/heap
|
|
|
|
# ---- Non-root user ----------------------------------------------------------
|
|
RUN groupadd --system --gid 1000 app \
|
|
&& useradd --system --uid 1000 --gid app --no-create-home --shell /usr/sbin/nologin app
|
|
|
|
WORKDIR /app
|
|
|
|
COPY --from=builder --chown=app:app /build/src/sample-portfolio/build/docker/application.jar app.jar
|
|
|
|
USER app
|
|
|
|
# ---- Ports ------------------------------------------------------------------
|
|
# 8080 — application HTTP port
|
|
# 9001 — management / actuator port
|
|
EXPOSE 8080 9001
|
|
|
|
# ---- Health check -----------------------------------------------------------
|
|
# Actuator readiness probe on the management port (9001). Ignored by Kubernetes,
|
|
# which uses its own probes — kept for docker/compose parity with src/Dockerfile.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
|
|
CMD wget --no-verbose --tries=1 --spider \
|
|
http://localhost:9001/actuator/health/readiness || exit 1
|
|
|
|
# ---- Entrypoint -------------------------------------------------------------
|
|
# mainClass (SamplePortfolioApplication) is baked into the bootJar manifest.
|
|
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
|