feat(setup): install newest supported JDK; repair stale JAVA_HOME

setup.sh hardcoded openjdk-17 and only ever checked `java -version`, so it
passed its own Java check on a box whose JAVA_HOME pointed at a JDK that had
been removed -- then died at `./gradlew --version` with the real cause
(JAVA_HOME is set to an invalid directory) swallowed by a /dev/null redirect.

- latest_installable_jdk() asks apt/dnf/pacman for the highest openjdk-N-jdk
  within [MIN_JAVA, MAX_JAVA]. MAX_JAVA=21 is the ceiling the gradle-8.5
  wrapper can run on; raise it alongside gradle-wrapper.properties.
- install path uses that version instead of a hardcoded 17, and an installed
  but older JDK now prompts for an upgrade rather than passing silently.
- fix_java_home() repoints JAVA_HOME at the JDK behind the java on PATH when
  the inherited one has no bin/java, and prints the permanent fix.
- version parser tolerates "Picked up _JAVA_OPTIONS" preamble and 1.8.0_x.
- missing git is fatal instead of being recorded and ignored; gradle's own
  error output survives a wrapper failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011thcefjGXbPwcgLmRqFWbb
This commit is contained in:
Paul Trowbridge 2026-08-09 16:30:47 -04:00
parent 2ced7810d9
commit cc8bf49565

172
setup.sh
View File

@ -17,18 +17,96 @@ if [[ "$OSTYPE" != "linux-gnu"* ]]; then
echo "" echo ""
fi fi
# Java versions this project builds with. MAX_JAVA is capped by the Gradle
# wrapper -- gradle-8.5 refuses to run on anything newer than JDK 21. Raise it
# when you bump gradle/wrapper/gradle-wrapper.properties.
MIN_JAVA=11
MAX_JAVA=21
GRADLE_WRAPPER_VERSION=$(sed -n 's/.*gradle-\([0-9.]*\)-bin\.zip/\1/p' \
gradle/wrapper/gradle-wrapper.properties 2>/dev/null)
# Function to check if command exists # Function to check if command exists
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" >/dev/null 2>&1
} }
# Function to get Java version # Function to get Java major version (8, 11, 17, ...); echoes 0 if undetectable
get_java_version() { get_java_version() {
if command_exists java; then local line full major
java -version 2>&1 | head -n 1 | awk -F '"' '{print $2}' | awk -F '.' '{print $1}' if ! command_exists java; then
else
echo "0" echo "0"
return
fi fi
# grep the version line rather than taking head -1: some JVMs prepend
# "Picked up _JAVA_OPTIONS: ..." when those env vars are set
line=$(java -version 2>&1 | grep -Ei '(openjdk|java|jre)[a-z ]* version' | head -n 1)
full=$(echo "$line" | awk -F '"' '{print $2}')
major=$(echo "$full" | awk -F '[._-]' '{print $1}')
# legacy "1.8.0_381" style -> 8
if [ "$major" = "1" ]; then
major=$(echo "$full" | awk -F '.' '{print $2}')
fi
case "$major" in
''|*[!0-9]*) major=0 ;;
esac
echo "$major"
}
# Highest OpenJDK major version offered by the package manager, within
# [MIN_JAVA, MAX_JAVA]. Echoes nothing if it can't find one.
latest_installable_jdk() {
local versions=""
if command_exists apt-cache; then
versions=$(apt-cache pkgnames openjdk- 2>/dev/null \
| sed -n 's/^openjdk-\([0-9]\+\)-jdk$/\1/p')
elif command_exists dnf; then
versions=$(dnf -q list --available 'java-*-openjdk-devel' 2>/dev/null \
| sed -n 's/^java-1\?\.\?\([0-9]\+\)[^ ]*-openjdk-devel\..*/\1/p')
elif command_exists pacman; then
versions=$(pacman -Ssq '^jdk[0-9]+-openjdk$' 2>/dev/null \
| sed -n 's/^jdk\([0-9]\+\)-openjdk$/\1/p')
fi
echo "$versions" | awk -v min="$MIN_JAVA" -v max="$MAX_JAVA" \
'$1 ~ /^[0-9]+$/ && $1 >= min && $1 <= max && $1 > best { best = $1 }
END { if (best) print best }'
}
# Install a given OpenJDK major version via the system package manager
install_jdk() {
local ver="$1"
if command_exists apt; then
sudo apt update && sudo apt install -y "openjdk-${ver}-jdk"
elif command_exists dnf; then
sudo dnf install -y "java-${ver}-openjdk-devel"
elif command_exists pacman; then
sudo pacman -S --noconfirm "jdk${ver}-openjdk"
else
echo -e "${RED}Could not detect package manager. Please install Java manually.${NC}"
return 1
fi
}
# The JDK directory holding the java on PATH
java_home_from_path() {
local j
j=$(command -v java) || return 1
j=$(readlink -f "$j") || return 1
dirname "$(dirname "$j")"
}
# Gradle honours JAVA_HOME ahead of PATH, so a JAVA_HOME left over from a
# hand-installed JDK breaks every build even when `java` itself works fine.
fix_java_home() {
local detected
detected=$(java_home_from_path) || return 0
[ -n "$JAVA_HOME" ] && [ -x "$JAVA_HOME/bin/java" ] && return 0
if [ -n "$JAVA_HOME" ]; then
echo -e "${YELLOW}!${NC} JAVA_HOME is set to ${JAVA_HOME}, which has no bin/java"
echo " Using ${detected} for this run. To fix it permanently, put this in"
echo " your shell profile (it follows update-alternatives, so it won't go stale):"
echo " export JAVA_HOME=\$(dirname \"\$(dirname \"\$(readlink -f \"\$(command -v java)\")\")\")"
fi
export JAVA_HOME="$detected"
} }
echo "Checking dependencies..." echo "Checking dependencies..."
@ -36,22 +114,23 @@ echo ""
# Check Java # Check Java
JAVA_VERSION=$(get_java_version) JAVA_VERSION=$(get_java_version)
if [ "$JAVA_VERSION" -ge 11 ]; then LATEST_JDK=$(latest_installable_jdk)
if [ "$JAVA_VERSION" -ge "$MIN_JAVA" ]; then
echo -e "${GREEN}${NC} Java $JAVA_VERSION detected" echo -e "${GREEN}${NC} Java $JAVA_VERSION detected"
java -version 2>&1 | head -n 1 java -version 2>&1 | grep -Ei '(openjdk|java|jre)[a-z ]* version' | head -n 1
JAVA_OK=true JAVA_OK=true
else else
echo -e "${RED}${NC} Java 11+ not found" echo -e "${RED}${NC} Java ${MIN_JAVA}+ not found"
JAVA_OK=false JAVA_OK=false
fi fi
# Check Git # Check Git
if command_exists git; then if command_exists git; then
echo -e "${GREEN}${NC} Git detected" echo -e "${GREEN}${NC} Git detected"
DEPENDENCIES_OK=true GIT_OK=true
else else
echo -e "${RED}${NC} Git not found" echo -e "${RED}${NC} Git not found"
DEPENDENCIES_OK=false GIT_OK=false
fi fi
# Check if gradlew exists # Check if gradlew exists
@ -65,45 +144,36 @@ fi
echo "" echo ""
# If Java is missing, offer installation help # Install the newest supported JDK, either because none is present or because
# the installed one is behind what the package manager offers
SUGGEST=${LATEST_JDK:-$MAX_JAVA}
if [ "$JAVA_OK" = false ]; then if [ "$JAVA_OK" = false ]; then
echo -e "${YELLOW}Java 11+ is required to build and run jrunner${NC}" echo -e "${YELLOW}Java ${MIN_JAVA}+ is required to build and run jrunner${NC}"
echo "" echo ""
echo "Installation options:" echo "Installation options:"
echo "" echo ""
echo "1. Install OpenJDK via package manager (recommended):" echo "1. Install OpenJDK via package manager (recommended):"
echo " Ubuntu/Debian: sudo apt update && sudo apt install openjdk-17-jdk" echo " Ubuntu/Debian: sudo apt update && sudo apt install openjdk-${SUGGEST}-jdk"
echo " Fedora/RHEL: sudo dnf install java-17-openjdk-devel" echo " Fedora/RHEL: sudo dnf install java-${SUGGEST}-openjdk-devel"
echo " Arch: sudo pacman -S jdk-openjdk" echo " Arch: sudo pacman -S jdk${SUGGEST}-openjdk"
echo "" echo ""
echo "2. Download Oracle JDK manually:" echo "2. Download a JDK tarball manually:"
echo " Visit: https://www.oracle.com/java/technologies/downloads/" echo " Visit: https://adoptium.net/temurin/releases/"
echo " Download JDK 17+ tarball and extract to /opt" echo " Download JDK ${MIN_JAVA}-${MAX_JAVA} and extract to /opt"
echo " Add to PATH: export JAVA_HOME=/opt/jdk-17 && export PATH=\$PATH:\$JAVA_HOME/bin" echo " export JAVA_HOME=/opt/jdk-${SUGGEST} && export PATH=\$PATH:\$JAVA_HOME/bin"
echo "" echo ""
read -p "Would you like to install OpenJDK via package manager? (requires sudo) [y/N]: " install_java read -p "Install OpenJDK ${SUGGEST} via package manager? (requires sudo) [y/N]: " install_java || install_java=""
if [[ "$install_java" =~ ^[Yy]$ ]]; then if [[ "$install_java" =~ ^[Yy]$ ]]; then
if command_exists apt; then echo "Installing OpenJDK ${SUGGEST}..."
echo "Installing OpenJDK 17 via apt..." install_jdk "$SUGGEST" || exit 1
sudo apt update
sudo apt install -y openjdk-17-jdk
elif command_exists dnf; then
echo "Installing OpenJDK 17 via dnf..."
sudo dnf install -y java-17-openjdk-devel
elif command_exists pacman; then
echo "Installing OpenJDK via pacman..."
sudo pacman -S --noconfirm jdk-openjdk
else
echo -e "${RED}Could not detect package manager. Please install Java manually.${NC}"
exit 1
fi
# Verify installation # a fresh install relocates java; re-derive JAVA_HOME from PATH
unset JAVA_HOME
JAVA_VERSION=$(get_java_version) JAVA_VERSION=$(get_java_version)
if [ "$JAVA_VERSION" -ge 11 ]; then if [ "$JAVA_VERSION" -ge "$MIN_JAVA" ]; then
echo -e "${GREEN}✓ Java installed successfully${NC}" echo -e "${GREEN}✓ Java $JAVA_VERSION installed successfully${NC}"
JAVA_OK=true JAVA_OK=true
else else
echo -e "${RED}✗ Java installation failed${NC}" echo -e "${RED}✗ Java installation failed${NC}"
@ -114,12 +184,30 @@ if [ "$JAVA_OK" = false ]; then
echo "Please install Java manually and re-run this script." echo "Please install Java manually and re-run this script."
exit 1 exit 1
fi fi
elif [ -n "$LATEST_JDK" ] && [ "$LATEST_JDK" -gt "$JAVA_VERSION" ]; then
echo -e "${YELLOW}!${NC} OpenJDK ${LATEST_JDK} is available (you are on ${JAVA_VERSION});"
echo " ${MAX_JAVA} is the newest the Gradle ${GRADLE_WRAPPER_VERSION:-8.5} wrapper can run on."
read -p "Upgrade to OpenJDK ${LATEST_JDK}? (requires sudo) [y/N]: " upgrade_java || upgrade_java=""
if [[ "$upgrade_java" =~ ^[Yy]$ ]]; then
echo "Installing OpenJDK ${LATEST_JDK}..."
if install_jdk "$LATEST_JDK"; then
unset JAVA_HOME
JAVA_VERSION=$(get_java_version)
echo -e "${GREEN}✓ Now on Java ${JAVA_VERSION}${NC}"
else
echo -e "${YELLOW}Upgrade failed; continuing with Java ${JAVA_VERSION}${NC}"
fi fi
fi
fi
# Point JAVA_HOME at a JDK that actually exists before invoking Gradle
fix_java_home
echo ""
# Check for unzip (needed for deployment) # Check for unzip (needed for deployment)
if ! command_exists unzip; then if ! command_exists unzip; then
echo -e "${YELLOW}Note: 'unzip' is recommended for deployment${NC}" echo -e "${YELLOW}Note: 'unzip' is recommended for deployment${NC}"
read -p "Install unzip? [y/N]: " install_unzip read -p "Install unzip? [y/N]: " install_unzip || install_unzip=""
if [[ "$install_unzip" =~ ^[Yy]$ ]]; then if [[ "$install_unzip" =~ ^[Yy]$ ]]; then
if command_exists apt; then if command_exists apt; then
sudo apt install -y unzip sudo apt install -y unzip
@ -132,7 +220,12 @@ if ! command_exists unzip; then
fi fi
echo "" echo ""
if [ "$GIT_OK" = false ]; then
echo -e "${RED}Error: git is required. Install it and re-run this script.${NC}"
exit 1
fi
echo -e "${GREEN}All required dependencies are installed!${NC}" echo -e "${GREEN}All required dependencies are installed!${NC}"
echo " Java ${JAVA_VERSION} @ ${JAVA_HOME:-$(java_home_from_path)}"
echo "" echo ""
# Verify we're in the right directory # Verify we're in the right directory
@ -145,10 +238,13 @@ fi
chmod +x ./gradlew chmod +x ./gradlew
echo "Testing build system..." echo "Testing build system..."
if ./gradlew --version > /dev/null 2>&1; then # keep the output: gradle's own message names the cause (bad JAVA_HOME, no
# network for the distribution download, ...) and a bare "failed" hides it
if GRADLE_OUT=$(./gradlew --version 2>&1); then
echo -e "${GREEN}${NC} Gradle wrapper working" echo -e "${GREEN}${NC} Gradle wrapper working"
else else
echo -e "${RED}${NC} Gradle wrapper failed" echo -e "${RED}${NC} Gradle wrapper failed"
echo "$GRADLE_OUT"
exit 1 exit 1
fi fi