Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/spring-oracle-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: spring-oracle-example CI Build

on:
pull_request:
branches: [master]
paths:
- "spring-oracle-example/**"
types:
- opened
- synchronize
- reopened
Comment on lines +3 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Quote the “on” key to satisfy YAML linters

Prevents YAML 1.1 truthy parsing complaints.

Apply:

-on:
+"on":
   pull_request:
     branches: [master]
     paths:
       - "spring-oracle-example/**"
     types:
       - opened
       - synchronize
       - reopened
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
on:
pull_request:
branches: [master]
paths:
- "spring-oracle-example/**"
types:
- opened
- synchronize
- reopened
"on":
pull_request:
branches: [master]
paths:
- "spring-oracle-example/**"
types:
- opened
- synchronize
- reopened
🧰 Tools
🪛 YAMLlint (1.37.1)

[warning] 3-3: truthy value should be one of [false, true]

(truthy)

🤖 Prompt for AI Agents
.github/workflows/spring-oracle-example.yml lines 3-11: the top-level YAML key
on is unquoted which can trigger YAML 1.1 truthy parsing linters; update the
file to quote the key (e.g., change on: to "on":) so the key is treated as a
string, preserving the existing pull_request block and behavior.


jobs:

integration-tests:
name: Run Unit & Integration Tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: spring-oracle-example
strategy:
matrix:
distribution: [ 'temurin' ]
java: [ '21' ]
Comment on lines +23 to +24
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fix bracket spacing in matrices (YAMLlint)

Compact arrays silence lint errors.

Apply:

-        distribution: [ 'temurin' ]
-        java: [ '21' ]
+        distribution: ["temurin"]
+        java: ["21"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
distribution: [ 'temurin' ]
java: [ '21' ]
distribution: ["temurin"]
java: ["21"]
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 23-23: too many spaces inside brackets

(brackets)


[error] 23-23: too many spaces inside brackets

(brackets)


[error] 24-24: too many spaces inside brackets

(brackets)


[error] 24-24: too many spaces inside brackets

(brackets)

🤖 Prompt for AI Agents
.github/workflows/spring-oracle-example.yml lines 23-24: the matrix arrays use
spaced bracket style which triggers YAMLlint; replace the current "distribution:
[ 'temurin' ]" and "java: [ '21' ]" with compact arrays (no spaces after '[' or
before ']'), e.g. "distribution: ['temurin']" and "java: ['21']".

steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v5.0.0
with:
java-version: ${{ matrix.java }}
distribution: ${{ matrix.distribution }}
cache: 'maven'
- name: Build and analyze
run: ./mvnw clean verify

health-check:
name: Health Check on Services
runs-on: ubuntu-latest
steps:
- name: Checkout repository and submodules
uses: actions/checkout@v5
with:
submodules: true

- name: Extract service names from docker compose
id: services
run: |
echo "services<<EOF" >> $GITHUB_OUTPUT
docker compose -f ./spring-oracle-example/compose.yaml config --services >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

Comment on lines +48 to +54
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden GITHUB_OUTPUT handling and normalize services list

Fail fast on errors and emit a space-delimited list to avoid quoting/newline pitfalls downstream.

-      - name: Extract service names from docker compose
+      - name: Extract service names from docker compose
         id: services
         run: |
-          echo "services<<EOF" >> $GITHUB_OUTPUT
-          docker compose -f ./spring-oracle-example/compose.yaml config --services >> $GITHUB_OUTPUT
-          echo "EOF" >> $GITHUB_OUTPUT
+          set -euo pipefail
+          services="$(docker compose -f ./spring-oracle-example/compose.yaml config --services | tr '\n' ' ' | sed -e 's/[[:space:]]\+/ /g' -e 's/[[:space:]]$//')"
+          echo "services=${services}" >> "$GITHUB_OUTPUT"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Extract service names from docker compose
id: services
run: |
echo "services<<EOF" >> $GITHUB_OUTPUT
docker compose -f ./spring-oracle-example/compose.yaml config --services >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Extract service names from docker compose
id: services
run: |
set -euo pipefail
services="$(docker compose -f ./spring-oracle-example/compose.yaml config --services | tr '\n' ' ' | sed -e 's/[[:space:]]\+/ /g' -e 's/[[:space:]]$//')"
echo "services=${services}" >> "$GITHUB_OUTPUT"
🤖 Prompt for AI Agents
In .github/workflows/spring-oracle-example.yml around lines 48-54, the step
writing services to GITHUB_OUTPUT should fail fast on errors and emit a single
space-delimited value instead of raw newlines; update the script to enable
strict shell mode (e.g., set -euo pipefail), run docker compose to capture the
service names into a variable, normalize newlines to spaces (trim
leading/trailing whitespace), verify the variable is non-empty and exit with an
error if it is, then write a single line like services=<space-delimited-list> to
$GITHUB_OUTPUT so downstream steps get a safe, quoted-free list.

- name: Start containers with Compose Action
uses: hoverkraft-tech/compose-action@v2.3.0
with:
compose-file: './spring-oracle-example/compose.yaml'
services: ${{ steps.services.outputs.services }}
up-flags: '--build'
down-flags: '--volumes'

Comment on lines +55 to +62
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pin third-party action and clean up orphans on down

Pin by commit SHA for security; remove orphaned containers on teardown to reduce cross-run interference.

-      - name: Start containers with Compose Action
-        uses: hoverkraft-tech/compose-action@v2.3.0
+      - name: Start containers with Compose Action
+        uses: hoverkraft-tech/compose-action@v2.3.0
+        # TODO: consider pinning to the action's commit SHA for supply-chain hardening
         with:
           compose-file: './spring-oracle-example/compose.yaml'
           services: ${{ steps.services.outputs.services }}
           up-flags: '--build'
-          down-flags: '--volumes'
+          down-flags: '--volumes --remove-orphans'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Start containers with Compose Action
uses: hoverkraft-tech/compose-action@v2.3.0
with:
compose-file: './spring-oracle-example/compose.yaml'
services: ${{ steps.services.outputs.services }}
up-flags: '--build'
down-flags: '--volumes'
- name: Start containers with Compose Action
uses: hoverkraft-tech/compose-action@v2.3.0
# TODO: consider pinning to the action's commit SHA for supply-chain hardening
with:
compose-file: './spring-oracle-example/compose.yaml'
services: ${{ steps.services.outputs.services }}
up-flags: '--build'
down-flags: '--volumes --remove-orphans'
🤖 Prompt for AI Agents
.github/workflows/spring-oracle-example.yml lines 55-62: the Compose Action is
using a floating tag and doesn't remove orphaned containers on teardown; update
the uses key to pin the action to a specific commit SHA (replace the tag with
hoverkraft-tech/compose-action@<commit-sha>) for security, and modify down-flags
to include --remove-orphans (e.g., '--volumes --remove-orphans') so orphaned
containers are cleaned up on down.

- name: Wait for containers to initialize
run: sleep 20

- name: Check container health
run: |
./.github/scripts/check-container-health.sh "${{ steps.services.outputs.services }}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace fixed sleep with a bounded health wait loop

Avoids flakiness on slow Oracle startup.

Apply:

-      - name: Wait for containers to initialize
-        run: sleep 20
-
-      - name: Check container health
-        run: |
-          ./.github/scripts/check-container-health.sh "${{ steps.services.outputs.services }}"
+      - name: Wait until services are healthy (max 5m)
+        run: |
+          set -e
+          for svc in ${{ steps.services.outputs.services }}; do
+            id="$(docker compose -f ./spring-oracle-example/compose.yaml ps -q "$svc")"
+            for i in {1..60}; do
+              status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}starting{{end}}' "$id" 2>/dev/null || echo starting)"
+              [ "$status" = "healthy" ] && break
+              sleep 5
+            done
+            docker inspect -f '{{.Name}} -> {{.State.Health.Status}}' "$id"
+          done
+
+      - name: Check container health
+        run: ./.github/scripts/check-container-health.sh "${{ steps.services.outputs.services }}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Wait for containers to initialize
run: sleep 20
- name: Check container health
run: |
./.github/scripts/check-container-health.sh "${{ steps.services.outputs.services }}"
- name: Wait until services are healthy (max 5m)
run: |
set -e
for svc in ${{ steps.services.outputs.services }}; do
id="$(docker compose -f ./spring-oracle-example/compose.yaml ps -q "$svc")"
for i in {1..60}; do
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}starting{{end}}' "$id" 2>/dev/null || echo starting)"
[ "$status" = "healthy" ] && break
sleep 5
done
docker inspect -f '{{.Name}} -> {{.State.Health.Status}}' "$id"
done
- name: Check container health
run: ./.github/scripts/check-container-health.sh "${{ steps.services.outputs.services }}"
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 68-68: no new line character at the end of file

(new-line-at-end-of-file)

🤖 Prompt for AI Agents
.github/workflows/spring-oracle-example.yml lines 63-68: currently a fixed 20s
sleep precedes the container health check which can be flaky for slow Oracle
startups; replace the sleep with a bounded wait loop that repeatedly invokes
./.github/scripts/check-container-health.sh (or queries the containers' health
endpoints) with short sleeps between attempts and a configurable overall
timeout/attempt limit, failing the job if health is not achieved within the
bound; implement the loop in the workflow step (or inside the script) so it
exits immediately on success and returns non-zero after the timeout to fail the
run.

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<module>spring-prometheus-grafana-example</module>
<module>spring-keycloak-example</module>
<module>spring-jasper-example</module>
<module>spring-oracle-example</module>
</modules>

</project>
62 changes: 62 additions & 0 deletions spring-oracle-example/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.DS_Store
**/.classpath
**/.dockerignore
**/.env
**/.factorypath
**/.git
**/.gitignore
**/.idea
**/.project
**/.sts4-cache
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.next
**/.cache
**/*.dbmdl
**/*.jfm
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/secrets.dev.yaml
**/values.dev.yaml
**/vendor
LICENSE
README.md
Comment on lines +32 to +33
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider keeping LICENSE/README in context.

Some multi-stage builds copy README/LICENSE into the image labels or docs; harmless to include given size.

-LICENSE
-README.md
+# LICENSE
+# README.md
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LICENSE
README.md
# LICENSE
# README.md
🤖 Prompt for AI Agents
In spring-oracle-example/.dockerignore around lines 32 to 33, the Docker ignore
file currently excludes LICENSE and README.md which can prevent multi-stage
builds from having those files available for image labels or documentation;
remove LICENSE and README.md from .dockerignore (or comment them out) so the
files are included in build context, then re-run the build to ensure multi-stage
copy/label steps can access them.

**/*.class
**/*.iml
**/*.ipr
**/*.iws
**/*.log
**/.apt_generated
**/.gradle
**/.gradletasknamecache
**/.nb-gradle
**/.springBeans
**/build
**/dist
**/gradle-app.setting
**/nbbuild
**/nbdist
**/nbproject/private
**/target
*.ctxt
.mtj.tmp
.mvn/timing.properties
buildNumber.properties
dependency-reduced-pom.xml
hs_err_pid*
pom.xml.next
pom.xml.releaseBackup
pom.xml.tag
pom.xml.versionsBackup
release.properties
replay_pid*
2 changes: 2 additions & 0 deletions spring-oracle-example/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/mvnw text eol=lf
*.cmd text eol=crlf
Comment on lines +1 to +2
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

EOL rules look good; add shell scripts for completeness.

Add LF for executable shell scripts to avoid CRLF issues on *nix.

 /mvnw text eol=lf
 *.cmd text eol=crlf
+*.sh text eol=lf
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/mvnw text eol=lf
*.cmd text eol=crlf
/mvnw text eol=lf
*.cmd text eol=crlf
*.sh text eol=lf
🤖 Prompt for AI Agents
In spring-oracle-example/.gitattributes lines 1-2, the file sets EOL rules for
mvnw and .cmd but omits executable shell scripts; add entries for common shell
script patterns (e.g. *.sh text eol=lf and optionally scripts/* text eol=lf or
bin/* text eol=lf) so that executable shell scripts use LF on Unix systems and
avoid CRLF issues.

30 changes: 30 additions & 0 deletions spring-oracle-example/.github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI Build

on:
push:
branches:
- "**"

jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
distribution: [ 'temurin' ]
java: [ '21' ]
steps:
- uses: actions/checkout@v5

- name: Setup Java 21
uses: actions/setup-java@v5
with:
java-version: ${{ matrix.java }}
distribution: ${{ matrix.distribution }}
cache: 'maven'

- name: Grant execute permission for mvnw
run: chmod +x mvnw

- name: Build with Maven
run: ./mvnw clean verify
32 changes: 32 additions & 0 deletions spring-oracle-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
target/
.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
2 changes: 2 additions & 0 deletions spring-oracle-example/.mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
47 changes: 47 additions & 0 deletions spring-oracle-example/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
FROM eclipse-temurin:21-jdk-jammy as deps

WORKDIR /build

COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/

RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests

FROM deps as package

WORKDIR /build

COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 \
./mvnw package -DskipTests && \
mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
Comment on lines +16 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Minor: avoid repeated Maven evaluations in build step

You invoke ./mvnw three extra times just to compute artifactId/version. Cache finalName once to reduce build time/log noise.

Apply:

-RUN --mount=type=bind,source=pom.xml,target=pom.xml \
-    --mount=type=cache,target=/root/.m2 \
-    ./mvnw package -DskipTests && \
-    mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
+RUN --mount=type=bind,source=pom.xml,target=pom.xml \
+    --mount=type=cache,target=/root/.m2 \
+    APP_NAME=$(./mvnw -q -DforceStdout help:evaluate -Dexpression=project.build.finalName) && \
+    ./mvnw -q package -DskipTests && \
+    mv "target/${APP_NAME}.jar" target/app.jar
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 \
./mvnw package -DskipTests && \
mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
--mount=type=cache,target=/root/.m2 \
APP_NAME=$(./mvnw -q -DforceStdout help:evaluate -Dexpression=project.build.finalName) && \
./mvnw -q package -DskipTests && \
mv "target/${APP_NAME}.jar" target/app.jar
🤖 Prompt for AI Agents
In spring-oracle-example/Dockerfile around lines 16-19, the build step calls
./mvnw multiple times to compute artifactId/version, causing extra work and log
noise; change it to evaluate the final artifact name only once (e.g. evaluate
project.build.finalName or compute artifactId and version once into a shell
variable) and then use that single evaluated name to copy/rename the produced
jar to target/app.jar, rather than re-running ./mvnw three times.


FROM package as extract

WORKDIR /build

RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted

FROM eclipse-temurin:21-jre-jammy AS final

ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser

COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./

EXPOSE 8080

ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
Comment on lines +27 to +47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix final image: copy as root, set WORKDIR, classpath, and only then drop to non-root

Current COPYs run as appuser into "/", and ENTRYPOINT lacks a classpath, so the container will fail to start. Move USER after COPY (or use --chown), set a working dir, copy to that dir, and use the Boot loader classpath.

Apply:

-FROM eclipse-temurin:21-jre-jammy AS final
-
-ARG UID=10001
-RUN adduser \
+FROM eclipse-temurin:21-jre-jammy AS final
+WORKDIR /app
+
+ARG UID=10001
+RUN adduser \
     --disabled-password \
     --gecos "" \
     --home "/nonexistent" \
     --shell "/sbin/nologin" \
     --no-create-home \
     --uid "${UID}" \
     appuser
-USER appuser
-
-COPY --from=extract build/target/extracted/dependencies/ ./
-COPY --from=extract build/target/extracted/spring-boot-loader/ ./
-COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
-COPY --from=extract build/target/extracted/application/ ./
+COPY --from=extract /build/target/extracted/dependencies/ /app/dependencies/
+COPY --from=extract /build/target/extracted/spring-boot-loader/ /app/spring-boot-loader/
+COPY --from=extract /build/target/extracted/snapshot-dependencies/ /app/snapshot-dependencies/
+COPY --from=extract /build/target/extracted/application/ /app/application/
 
 EXPOSE 8080
-
-ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
+USER appuser
+ENTRYPOINT ["java","-cp","/app:/app/dependencies/*:/app/snapshot-dependencies/*:/app/spring-boot-loader/*","org.springframework.boot.loader.launch.JarLauncher"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
FROM eclipse-temurin:21-jre-jammy AS final
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
FROM eclipse-temurin:21-jre-jammy AS final
WORKDIR /app
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
COPY --from=extract /build/target/extracted/dependencies/ /app/dependencies/
COPY --from=extract /build/target/extracted/spring-boot-loader/ /app/spring-boot-loader/
COPY --from=extract /build/target/extracted/snapshot-dependencies/ /app/snapshot-dependencies/
COPY --from=extract /build/target/extracted/application/ /app/application/
EXPOSE 8080
USER appuser
ENTRYPOINT ["java","-cp","/app:/app/dependencies/*:/app/snapshot-dependencies/*:/app/spring-boot-loader/*","org.springframework.boot.loader.JarLauncher"]
🧰 Tools
🪛 Checkov (3.2.334)

[low] 1-47: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)

Comment on lines +45 to +47
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add a container HEALTHCHECK (optional but recommended)

A simple HTTP probe improves ops. If you don’t use Actuator, tweak the URL.

Apply:

 EXPOSE 8080
+HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=5 \
+  CMD wget -qO- http://127.0.0.1:8080/actuator/health | grep -q '"status":"UP"' || exit 1
 
 USER appuser

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Checkov (3.2.334)

[low] 1-47: Ensure that HEALTHCHECK instructions have been added to container images

(CKV_DOCKER_2)

🤖 Prompt for AI Agents
In spring-oracle-example/Dockerfile around lines 45-47, the image lacks a Docker
HEALTHCHECK; add a HEALTHCHECK Dockerfile instruction after the
EXPOSE/ENTRYPOINT that performs an HTTP probe (e.g., curl or wget against
http://localhost:8080/actuator/health or / if Actuator is not used) using
CMD-SHELL, and include sensible options (--interval, --timeout, --start-period,
--retries) so the container reports unhealthy when the app fails to respond;
ensure the probe returns an appropriate success exit code and adjust the URL if
your app exposes a different health endpoint.

32 changes: 32 additions & 0 deletions spring-oracle-example/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:

app:
build:
context: .
restart: unless-stopped
depends_on:
- oracle-db
environment:
Comment on lines +7 to +9
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Wait for DB readiness using healthchecks.

depends_on alone doesn’t wait for Oracle to accept connections; add service healthcheck and condition.

Apply:

   app:
@@
-    depends_on:
-      - oracle-db
+    depends_on:
+      oracle-db:
+        condition: service_healthy
@@
   oracle-db:
@@
+    healthcheck:
+      test: ["CMD-SHELL", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s SYSTEM:${ORACLE_PASSWORD}@localhost:1521/XEPDB1 | grep -q 1"]
+      interval: 10s
+      timeout: 5s
+      retries: 30
+      start_period: 60s

Also applies to: 22-32

🤖 Prompt for AI Agents
In spring-oracle-example/compose.yaml around lines 7-9 (and similarly for lines
22-32), the compose uses depends_on which does not wait for Oracle to accept
connections; add a healthcheck to the oracle-db service that probes the DB
(e.g., runs a lightweight sqlplus or TCP check) with
interval/retries/start_period, then change the depends_on entry for the
application service to use condition: service_healthy so the app only starts
after the DB healthcheck passes; ensure the healthcheck command and timing are
appropriate for Oracle startup so the service becomes healthy only when
connections are accepted.

SPRING_PROFILES_ACTIVE: default
SERVER_PORT: 8080
SPRING_DATASOURCE_URL: jdbc:oracle:thin:@oracle-db:1521/XEPDB1
SPRING_DATASOURCE_USERNAME: TEST_SCHEMA
SPRING_DATASOURCE_PASSWORD: Ag101Pwd123
SPRING_DATASOURCE_DRIVER: oracle.jdbc.OracleDriver
SPRING_JPA_HIBERNATE_DDL_AUTO: update
SPRING_JPA_SHOW_SQL: true
SPRING_HIBERNATE_DIALECT: org.hibernate.dialect.OracleDialect
Comment on lines +10 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

⚠️ Potential issue

Hard-coded credentials in Compose; externalize and require via .env.

Avoid committing secrets; enforce presence at runtime.

Apply:

     environment:
       SPRING_PROFILES_ACTIVE: default
       SERVER_PORT: 8080
       SPRING_DATASOURCE_URL: jdbc:oracle:thin:@oracle-db:1521/XEPDB1
-      SPRING_DATASOURCE_USERNAME: TEST_SCHEMA
-      SPRING_DATASOURCE_PASSWORD: Ag101Pwd123
+      SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME:?required}
+      SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:?required}
       SPRING_DATASOURCE_DRIVER: oracle.jdbc.OracleDriver
       SPRING_JPA_HIBERNATE_DDL_AUTO: update
       SPRING_JPA_SHOW_SQL: true
       SPRING_HIBERNATE_DIALECT: org.hibernate.dialect.OracleDialect
@@
     environment:
-      ORACLE_PASSWORD: "Admin123"
+      ORACLE_PASSWORD: ${ORACLE_PASSWORD:?required}

Add an .env.example (checked-in) and a real .env (gitignored) with the three variables.

Want me to generate .env.example and update .gitignore?

Also applies to: 28-29

🧰 Tools
🪛 Checkov (3.2.334)

[low] 14-15: Base64 High Entropy String

(CKV_SECRET_6)

🤖 Prompt for AI Agents
In spring-oracle-example/compose.yaml around lines 10-18 (and also lines 28-29),
the compose file contains hard-coded DB credentials and other secrets; replace
those literal values with environment variable references (e.g. use
${SPRING_DATASOURCE_URL}, ${SPRING_DATASOURCE_USERNAME},
${SPRING_DATASOURCE_PASSWORD}) and load them via docker-compose env_file or
variable substitution. Add a checked-in .env.example listing the required keys
(SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD)
with placeholder values, add a real .env to .gitignore, and update README or
compose usage to require the .env be populated before running; optionally add a
small runtime check (or shell script) to fail fast if required vars are missing.

ports:
- "8080:8080"

oracle-db:
image: gvenzl/oracle-xe:21-slim
container_name: oracle-xe
restart: unless-stopped
ports:
- "1521:1521"
environment:
ORACLE_PASSWORD: "Admin123"
shm_size: 1g
volumes:
- ./docker/oracle:/docker-entrypoint-initdb.d
Comment on lines +1 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add trailing newline.

Fix linter complaint.

🧰 Tools
🪛 Checkov (3.2.334)

[low] 14-15: Base64 High Entropy String

(CKV_SECRET_6)

🪛 YAMLlint (1.37.1)

[error] 32-32: no new line character at the end of file

(new-line-at-end-of-file)

🤖 Prompt for AI Agents
In spring-oracle-example/compose.yaml around lines 1 to 32 the file is missing a
trailing newline which triggers the linter; fix by adding a single newline
character at the end of the file (ensure it's a proper LF newline) and save the
file so the last line ends with a newline.

5 changes: 5 additions & 0 deletions spring-oracle-example/docker/oracle/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER SESSION SET CONTAINER = XEPDB1;
CREATE USER TEST_SCHEMA IDENTIFIED BY Ag101Pwd123;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Plaintext password committed.

Keep for demo if intentional, but prefer sourcing from env/secrets during init to avoid teaching bad practices.

🤖 Prompt for AI Agents
In spring-oracle-example/docker/oracle/init.sql around lines 2 to 2, a plaintext
password (Ag101Pwd123) is committed in the CREATE USER statement; change this to
read the password from a safe source during initialization (e.g., use an
environment variable or a placeholder populated at container startup). Replace
the hardcoded literal with a variable placeholder and update
Dockerfile/entrypoint to inject the password from an environment secret (or
mount a secrets file) before executing init.sql, and ensure example docs mention
how to supply the secret for local/demo use.

GRANT CREATE SESSION TO TEST_SCHEMA;
GRANT CONNECT, RESOURCE TO TEST_SCHEMA;
ALTER USER TEST_SCHEMA QUOTA UNLIMITED ON USERS;
Comment on lines +1 to +5
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid deprecated/over-privileged roles and confirm container context.

  • CONNECT/RESOURCE are legacy and can over-grant. Grant specific privileges instead; limit quotas.
  • Depending on the Oracle XE image, this script may already run inside XEPDB1; ALTER SESSION SET CONTAINER = XEPDB1; could fail if not executed from CDB$ROOT. Verify execution context.

Would you like me to tailor this for your exact Oracle image? Proposed revision:

-ALTER SESSION SET CONTAINER = XEPDB1;
-CREATE USER TEST_SCHEMA IDENTIFIED BY Ag101Pwd123;
-GRANT CREATE SESSION TO TEST_SCHEMA;
-GRANT CONNECT, RESOURCE TO TEST_SCHEMA;
-ALTER USER TEST_SCHEMA QUOTA UNLIMITED ON USERS;
+-- WHENEVER SQLERROR EXIT SQL.SQLCODE
+-- Switch container only if running in CDB$ROOT
+-- ALTER SESSION SET CONTAINER = XEPDB1;
+CREATE USER TEST_SCHEMA IDENTIFIED BY "Ag101Pwd123";
+GRANT CREATE SESSION TO TEST_SCHEMA;
+GRANT CREATE TABLE, CREATE SEQUENCE, CREATE VIEW, CREATE TRIGGER, CREATE PROCEDURE TO TEST_SCHEMA;
+ALTER USER TEST_SCHEMA QUOTA 200M ON USERS;

🌐 Web query:

For the Docker image commonly used for local Oracle XE (e.g., gvenzl/oracle-xe), where do init scripts in /docker-entrypoint-initdb.d execute (CDB$ROOT or XEPDB1), and is ALTER SESSION SET CONTAINER needed/valid there?

💡 Result:

Short answer: The image runs init SQL as SYSDBA in the CDB root by default, but you can switch to the pluggable DB inside those scripts. So:

  • init scripts are executed connected to the CDB root (SYSDBA). [1][2]
  • ALTER SESSION SET CONTAINER = XEPDB1 is valid when run from those init scripts (you are running as SYSDBA/common user) and is the recommended way to target XEPDB1 from an init script. Example usage appears in the gvenzl repo/discussion. [2][3]

Sources:

  • Oracle docs: ALTER SESSION SET CONTAINER / multi‑tenant behavior. [1]
  • gvenzl (oci-oracle-xe) README and issues showing init scripts run as SYSDBA and that switching to XEPDB1 in scripts works. [2][3]

References:
[1] Oracle "Administering a CDB" / ALTER SESSION SET CONTAINER.
[2] gvenzl/oci-oracle-xe README (scripts run as SYSDBA and /docker-entrypoint-initdb.d behavior).
[3] gvenzl GitHub issue/discussion showing example init script using ALTER SESSION SET CONTAINER = XEPDB1.


Keep the container switch; remove legacy roles and limit quotas

  • File: spring-oracle-example/docker/oracle/init.sql (lines 1-5) — ALTER SESSION SET CONTAINER = XEPDB1 is valid for the common gvenzl Oracle XE init behavior (init scripts run as SYSDBA in the CDB root); keep it to target XEPDB1.
  • Replace GRANT CONNECT, RESOURCE with explicit, minimal grants. Keep GRANT CREATE SESSION and add only required object privileges (examples: CREATE TABLE, CREATE SEQUENCE, CREATE VIEW, CREATE TRIGGER, CREATE PROCEDURE).
  • Avoid UNLIMITED quotas — set a bounded quota (e.g., ALTER USER TEST_SCHEMA QUOTA 200M ON USERS) or give precise tablespace quotas.
  • Do not commit plaintext passwords in the repo; use environment variables or Docker secrets.
🤖 Prompt for AI Agents
In spring-oracle-example/docker/oracle/init.sql around lines 1-5, keep the
"ALTER SESSION SET CONTAINER = XEPDB1;" line, but replace the legacy GRANT
CONNECT, RESOURCE with explicit minimal grants (keep GRANT CREATE SESSION and
add only needed object privileges such as CREATE TABLE, CREATE SEQUENCE, CREATE
VIEW, CREATE TRIGGER, CREATE PROCEDURE), change the UNLIMITED quota to a bounded
quota (for example ALTER USER TEST_SCHEMA QUOTA 200M ON USERS or set precise
tablespace quotas), and remove the plaintext password from the script—instead
obtain the password from an environment variable or Docker secret at runtime.

Loading
Loading