#!/usr/bin/env sh set -eu JAR_PATH="${EASYFLOW_JAR_PATH:-/app/artifacts/easyflow.jar}" CONFIG_PATH="${EASYFLOW_CONFIG_PATH:-file:/app/application.yml}" LOG_FILE="${EASYFLOW_LOG_FILE:-/app/logs/app.log}" RESTART_GRACE_SECONDS="${EASYFLOW_JAR_RESTART_GRACE_SECONDS:-30}" ENTRYPOINT_PRIVS_DROPPED="${EASYFLOW_ENTRYPOINT_PRIVS_DROPPED:-0}" JAVA_PID="" WATCHER_PID="" STOP_REQUESTED=0 RESTART_REASON_FILE="/tmp/easyflow-restart-reason" log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" } ensure_runtime_permissions() { for path in /app/logs /app/data; do mkdir -p "$path" chown -R easyflow:easyflow "$path" done } drop_privileges_if_needed() { if [ "$(id -u)" -ne 0 ] || [ "$ENTRYPOINT_PRIVS_DROPPED" = "1" ]; then return fi ensure_runtime_permissions export EASYFLOW_ENTRYPOINT_PRIVS_DROPPED=1 exec setpriv --reuid=easyflow --regid=easyflow --init-groups "$0" "$@" } ensure_prerequisites() { if ! command -v inotifywait >/dev/null 2>&1; then log "ERROR: inotifywait not found" exit 1 fi if [ ! -f "$JAR_PATH" ]; then log "ERROR: easyflow jar not found: $JAR_PATH" exit 1 fi mkdir -p "$(dirname "$LOG_FILE")" } compute_jar_digest() { sha256sum "$JAR_PATH" | awk '{print $1}' } stop_watcher() { if [ -n "$WATCHER_PID" ] && kill -0 "$WATCHER_PID" 2>/dev/null; then kill "$WATCHER_PID" 2>/dev/null || true wait "$WATCHER_PID" 2>/dev/null || true fi WATCHER_PID="" } stop_java_process() { if [ -z "$JAVA_PID" ] || ! kill -0 "$JAVA_PID" 2>/dev/null; then return fi kill -TERM "$JAVA_PID" 2>/dev/null || true remaining="$RESTART_GRACE_SECONDS" while kill -0 "$JAVA_PID" 2>/dev/null; do if [ "$remaining" -le 0 ]; then log "java process did not stop within ${RESTART_GRACE_SECONDS}s, forcing shutdown" kill -KILL "$JAVA_PID" 2>/dev/null || true break fi sleep 1 remaining=$((remaining - 1)) done } handle_shutdown() { STOP_REQUESTED=1 printf '%s' "shutdown" >"$RESTART_REASON_FILE" stop_watcher stop_java_process } trap 'handle_shutdown' TERM INT start_java() { log "starting jar: $JAR_PATH" # JAVA_OPTS needs shell word splitting to preserve user-provided JVM args. # shellcheck disable=SC2086 java ${JAVA_OPTS:-} -Djava.security.egd=file:/dev/./urandom -jar "$JAR_PATH" \ --spring.config.location="$CONFIG_PATH" \ --logging.file.name="$LOG_FILE" & JAVA_PID=$! } start_watcher() { jar_digest="$1" jar_dir="$(dirname "$JAR_PATH")" jar_name="$(basename "$JAR_PATH")" ( while true; do event_line="$(inotifywait --quiet --event close_write,moved_to --format '%e %f' "$jar_dir")" || exit 0 event_name="${event_line%% *}" event_file="${event_line#* }" if [ "$event_file" != "$jar_name" ]; then continue fi if [ ! -f "$JAR_PATH" ]; then continue fi new_digest="$(compute_jar_digest)" if [ "$new_digest" = "$jar_digest" ]; then continue fi log "detected jar update (${event_name}), restarting java process" printf '%s' "jar_changed" >"$RESTART_REASON_FILE" stop_java_process exit 0 done ) & WATCHER_PID=$! } main() { drop_privileges_if_needed "$@" ensure_prerequisites current_digest="$(compute_jar_digest)" while true; do rm -f "$RESTART_REASON_FILE" start_java start_watcher "$current_digest" set +e wait "$JAVA_PID" java_status=$? set -e stop_watcher JAVA_PID="" restart_reason="" if [ -f "$RESTART_REASON_FILE" ]; then restart_reason="$(cat "$RESTART_REASON_FILE")" rm -f "$RESTART_REASON_FILE" fi if [ "$STOP_REQUESTED" -eq 1 ] || [ "$restart_reason" = "shutdown" ]; then exit 0 fi if [ "$restart_reason" = "jar_changed" ]; then ensure_prerequisites current_digest="$(compute_jar_digest)" log "restarting java with updated jar" continue fi log "java process exited unexpectedly with code $java_status" exit "$java_status" done } main "$@"