#!/usr/bin/env bash # Shared environment. Point JAVA_HOME at a JDK 25 (or newer) installation. : "${JAVA_HOME:?set JAVA_HOME to a JDK 25+ installation}" export PATH="$JAVA_HOME/bin:$PATH" MVN="${MVN:-mvn}" JAR="target/profiles-and-config-1.0.0.jar" APP_MAIN="com.ankurm.profiles.ProfilesApplication" APP_PORT="${APP_PORT:-8080}" # Strip environment noise that is an artefact of the machine, not of Spring: # the JVM prints a JAVA_TOOL_OPTIONS banner to stderr on every launch when a proxy # truststore is configured, and it would otherwise end up in every committed transcript. clean() { grep -v "Picked up JAVA_TOOL_OPTIONS" | grep -v "^OpenJDK 64-Bit Server VM warning"; } # Start the demo jar detached, record its PID, and block until it answers. # Extra arguments are passed to the application. Environment variables for the run are # passed by setting them on the call: `APP_ENV="A=1 B=2" start_app --spring.profiles.active=x` start_app() { stop_app mkdir -p target # Deliberately NOT setsid: setsid forks when it is not already a process-group leader, # so $! would be the PID of a process that exits immediately and the JVM would survive # every later stop_app. A surviving JVM keeps the port, the next scenario fails to bind, # and curl answers from the previous scenario -- which reads exactly like the # configuration change under test having had no effect. Three wrong findings in this # repository came from that before it was tracked down. if [ -n "${APP_ENV:-}" ]; then # shellcheck disable=SC2086 env $APP_ENV nohup java -jar "$JAR" "$@" > /tmp/profiles-demo.log 2>&1 < /dev/null & else nohup java -jar "$JAR" "$@" > /tmp/profiles-demo.log 2>&1 < /dev/null & fi echo $! > target/app.pid for _ in $(seq 1 60); do curl -s -o /dev/null "http://127.0.0.1:${APP_PORT}/precedence" 2>/dev/null && return 0 kill -0 "$(cat target/app.pid)" 2>/dev/null || { echo "JVM exited during startup:" tail -20 /tmp/profiles-demo.log; return 1; } sleep 1 done echo "application did not answer"; tail -20 /tmp/profiles-demo.log; return 1 } # Stop it by recorded PID. Never by pattern: `ps | grep ` also matches the shell # running the script, because the jar name is on that shell's own command line. stop_app() { if [ -f target/app.pid ]; then pid=$(cat target/app.pid) if [ -n "$pid" ] && grep -qa "profiles-and-config" "/proc/$pid/cmdline" 2>/dev/null; then kill -9 "$pid" 2>/dev/null || true wait "$pid" 2>/dev/null || true # reap, so bash prints no "Killed" notice fi rm -f target/app.pid fi for _ in $(seq 1 40); do if ! (exec 3<>/dev/tcp/127.0.0.1/"${APP_PORT:-8080}") 2>/dev/null; then break; fi sleep 0.25 done exec 3<&- 2>/dev/null || true } # Print the precedence report for one property, compactly. report() { curl -s "http://127.0.0.1:${APP_PORT}/precedence?name=$1" | python3 -c ' import json,sys d=json.load(sys.stdin) print(" active profiles :", ", ".join(d["activeProfiles"]) or "(none)") print(" effective value :", d["effectiveValue"]) for h in d["holders"]: src=h["source"] for noisy,short in (("Config resource \x27class path resource [","file "), ("\x27 via location \x27optional:classpath:/\x27}","")): src=src.replace(noisy,short) src=src.replace("OriginTrackedMapPropertySource {name=","").replace("]","") print(" %d. %-34s <- %s" % (h["rank"], h["value"], src.strip())) print(" holders that lost:", d["shadowedCount"])' }