diff --git a/docker/backend/Dockerfile b/docker/backend/Dockerfile index 0d5a310c45..168b5376df 100644 --- a/docker/backend/Dockerfile +++ b/docker/backend/Dockerfile @@ -89,12 +89,11 @@ RUN set -eux; \ RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version # Environment variables -ENV VERSION_TAG=$VERSION_TAG \ +ENV \ STIRLING_AOT_ENABLE="false" \ STIRLING_JVM_PROFILE="balanced" \ _JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ _JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ - JAVA_CUSTOM_OPTS="" \ HOME=/home/stirlingpdfuser \ PUID=1000 \ PGID=1000 \ diff --git a/docker/embedded/Dockerfile b/docker/embedded/Dockerfile index 7289d67bb5..fc63567193 100644 --- a/docker/embedded/Dockerfile +++ b/docker/embedded/Dockerfile @@ -106,12 +106,11 @@ RUN set -eux; \ RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version # Environment variables -ENV VERSION_TAG=$VERSION_TAG \ +ENV \ STIRLING_AOT_ENABLE="false" \ STIRLING_JVM_PROFILE="balanced" \ _JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ _JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ - JAVA_CUSTOM_OPTS="" \ HOME=/home/stirlingpdfuser \ PUID=1000 \ PGID=1000 \ diff --git a/docker/embedded/Dockerfile.fat b/docker/embedded/Dockerfile.fat index 542bdec981..fb326d5e05 100644 --- a/docker/embedded/Dockerfile.fat +++ b/docker/embedded/Dockerfile.fat @@ -105,12 +105,11 @@ RUN set -eux; \ RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version # Environment variables -ENV VERSION_TAG=$VERSION_TAG \ +ENV \ STIRLING_AOT_ENABLE="false" \ STIRLING_JVM_PROFILE="balanced" \ _JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ _JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ - JAVA_CUSTOM_OPTS="" \ HOME=/home/stirlingpdfuser \ PUID=1000 \ PGID=1000 \ diff --git a/docker/embedded/Dockerfile.ultra-lite b/docker/embedded/Dockerfile.ultra-lite index c21ddf2f8d..68366900b5 100644 --- a/docker/embedded/Dockerfile.ultra-lite +++ b/docker/embedded/Dockerfile.ultra-lite @@ -83,15 +83,17 @@ LABEL org.opencontainers.image.title="Stirling-PDF Ultra-Lite" \ org.opencontainers.image.version="${VERSION_TAG}" \ org.opencontainers.image.keywords="PDF, manipulation, ultra-lite, API, Spring Boot, React" +# Version file for scripts (init-without-ocr.sh reads /etc/stirling_version). +RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version + # Environment Variables # NOTE: Memory flags (InitialRAMPercentage, MaxRAMPercentage, MaxMetaspaceSize) # are computed dynamically by init-without-ocr.sh based on container memory limits. -ENV VERSION_TAG=$VERSION_TAG \ +ENV \ STIRLING_AOT_ENABLE="false" \ STIRLING_JVM_PROFILE="balanced" \ _JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ _JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \ - JAVA_CUSTOM_OPTS="" \ HOME=/home/stirlingpdfuser \ PUID=1000 \ PGID=1000 \ diff --git a/scripts/init-without-ocr.sh b/scripts/init-without-ocr.sh index c55dc1e6d4..bc26364b43 100755 --- a/scripts/init-without-ocr.sh +++ b/scripts/init-without-ocr.sh @@ -382,10 +382,13 @@ start_unoserver_pool() { } # ---------- VERSION_TAG ---------- -# Load VERSION_TAG from file if not provided via environment. -if [ -z "${VERSION_TAG:-}" ] && [ -f /etc/stirling_version ]; then - VERSION_TAG="$(tr -d '\r\n' < /etc/stirling_version)" - export VERSION_TAG +# The version baked into the image wins over a VERSION_TAG carried over from a previous +# container by the container manager. +INIT_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -r "${INIT_SCRIPT_DIR}/resolve-version-tag.sh" ]; then + # shellcheck source=resolve-version-tag.sh + . "${INIT_SCRIPT_DIR}/resolve-version-tag.sh" + resolve_version_tag fi # ---------- AOT ---------- diff --git a/scripts/resolve-version-tag.sh b/scripts/resolve-version-tag.sh new file mode 100644 index 0000000000..d49aae62a9 --- /dev/null +++ b/scripts/resolve-version-tag.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Resolve the version of the image that is actually running. +# +# The version baked into the image at build time wins over VERSION_TAG from the +# environment. Container managers (Portainer, Synology, and friends) carry the previous +# container's environment across an image update, so a VERSION_TAG left behind there +# used to mask the release that was really pulled - see issue #3460. The environment is +# only consulted when the image has no version file, which keeps dev and source builds +# working. +STIRLING_VERSION_FILE="${STIRLING_VERSION_FILE:-/etc/stirling_version}" + +resolve_version_tag() { + local baked="" + if [ -r "${STIRLING_VERSION_FILE}" ]; then + baked="$(tr -d '\r\n' < "${STIRLING_VERSION_FILE}")" + fi + + if [ -n "$baked" ]; then + if [ -n "${VERSION_TAG:-}" ] && [ "${VERSION_TAG}" != "$baked" ]; then + printf '%s\n' \ + "[init][warn] Ignoring VERSION_TAG=${VERSION_TAG} from the environment; this image is ${baked}." \ + "[init][warn] Remove VERSION_TAG from your container configuration - it is set by the image." >&2 + fi + VERSION_TAG="$baked" + export VERSION_TAG + elif [ -n "${VERSION_TAG:-}" ]; then + export VERSION_TAG + fi +} diff --git a/testing/test_resolve_version_tag.sh b/testing/test_resolve_version_tag.sh new file mode 100644 index 0000000000..b9aedac60e --- /dev/null +++ b/testing/test_resolve_version_tag.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# Unit tests for scripts/resolve-version-tag.sh (issue #3460). +# +# Container managers such as Portainer and Synology carry the previous container's +# environment across an image update. A VERSION_TAG left behind that way used to win +# over the version baked into the new image, so the app kept reporting - and, on the +# images that still downloaded a per-version jar, kept running - the old release. +set -uo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +HELPER="${REPO_ROOT}/scripts/resolve-version-tag.sh" + +failures=0 + +run_case() { + local name="$1" baked="$2" env_value="$3" expected="$4" + local tmp actual + tmp="$(mktemp -d)" + + if [ "$baked" != "" ]; then + printf '%s\n' "$baked" > "${tmp}/stirling_version" + fi + + actual="$( + set +u + export STIRLING_VERSION_FILE="${tmp}/stirling_version" + if [ "$env_value" != "" ]; then + export VERSION_TAG="$env_value" + else + unset VERSION_TAG + fi + # shellcheck source=/dev/null + . "$HELPER" + resolve_version_tag 2>/dev/null + printf '%s' "${VERSION_TAG:-}" + )" + + rm -rf "$tmp" + + if [ "$actual" = "$expected" ]; then + printf 'ok - %s\n' "$name" + else + printf 'FAIL - %s (expected %s, got %s)\n' "$name" "$expected" "$actual" + failures=$((failures + 1)) + fi +} + +if [ ! -r "$HELPER" ]; then + printf 'FAIL - %s is missing\n' "$HELPER" + exit 1 +fi + +run_case "baked version wins over a stale environment value" "2.14.3" "0.44.0" "2.14.3" +run_case "baked version is used when nothing is in the environment" "2.14.3" "" "2.14.3" +run_case "matching environment value is kept" "2.14.3" "2.14.3" "2.14.3" +run_case "environment value is honoured when the image has no version file" "" "0.44.0" "0.44.0" +run_case "empty version file does not blank out the environment value" "" "0.44.0" "0.44.0" +run_case "no version anywhere leaves VERSION_TAG unset" "" "" "" + +if [ "$failures" -gt 0 ]; then + printf '\n%d test(s) failed\n' "$failures" + exit 1 +fi +printf '\nall tests passed\n'