From cc8bf495650bfd6ac1f8726433d92aa23b2535a2 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Sun, 9 Aug 2026 16:30:47 -0400 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_011thcefjGXbPwcgLmRqFWbb --- setup.sh | 172 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 38 deletions(-) diff --git a/setup.sh b/setup.sh index 6ffb402..8ee78be 100755 --- a/setup.sh +++ b/setup.sh @@ -17,18 +17,96 @@ if [[ "$OSTYPE" != "linux-gnu"* ]]; then echo "" 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 command_exists() { 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() { - if command_exists java; then - java -version 2>&1 | head -n 1 | awk -F '"' '{print $2}' | awk -F '.' '{print $1}' - else + local line full major + if ! command_exists java; then echo "0" + return 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..." @@ -36,22 +114,23 @@ echo "" # Check Java 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" - 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 else - echo -e "${RED}✗${NC} Java 11+ not found" + echo -e "${RED}✗${NC} Java ${MIN_JAVA}+ not found" JAVA_OK=false fi # Check Git if command_exists git; then echo -e "${GREEN}✓${NC} Git detected" - DEPENDENCIES_OK=true + GIT_OK=true else echo -e "${RED}✗${NC} Git not found" - DEPENDENCIES_OK=false + GIT_OK=false fi # Check if gradlew exists @@ -65,45 +144,36 @@ fi 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 - 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 "Installation options:" echo "" echo "1. Install OpenJDK via package manager (recommended):" - echo " Ubuntu/Debian: sudo apt update && sudo apt install openjdk-17-jdk" - echo " Fedora/RHEL: sudo dnf install java-17-openjdk-devel" - echo " Arch: sudo pacman -S jdk-openjdk" + echo " Ubuntu/Debian: sudo apt update && sudo apt install openjdk-${SUGGEST}-jdk" + echo " Fedora/RHEL: sudo dnf install java-${SUGGEST}-openjdk-devel" + echo " Arch: sudo pacman -S jdk${SUGGEST}-openjdk" echo "" - echo "2. Download Oracle JDK manually:" - echo " Visit: https://www.oracle.com/java/technologies/downloads/" - echo " Download JDK 17+ tarball and extract to /opt" - echo " Add to PATH: export JAVA_HOME=/opt/jdk-17 && export PATH=\$PATH:\$JAVA_HOME/bin" + echo "2. Download a JDK tarball manually:" + echo " Visit: https://adoptium.net/temurin/releases/" + echo " Download JDK ${MIN_JAVA}-${MAX_JAVA} and extract to /opt" + echo " export JAVA_HOME=/opt/jdk-${SUGGEST} && export PATH=\$PATH:\$JAVA_HOME/bin" 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 command_exists apt; then - echo "Installing OpenJDK 17 via apt..." - 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 + echo "Installing OpenJDK ${SUGGEST}..." + install_jdk "$SUGGEST" || exit 1 - # Verify installation + # a fresh install relocates java; re-derive JAVA_HOME from PATH + unset JAVA_HOME JAVA_VERSION=$(get_java_version) - if [ "$JAVA_VERSION" -ge 11 ]; then - echo -e "${GREEN}✓ Java installed successfully${NC}" + if [ "$JAVA_VERSION" -ge "$MIN_JAVA" ]; then + echo -e "${GREEN}✓ Java $JAVA_VERSION installed successfully${NC}" JAVA_OK=true else 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." exit 1 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 +# Point JAVA_HOME at a JDK that actually exists before invoking Gradle +fix_java_home +echo "" + # Check for unzip (needed for deployment) if ! command_exists unzip; then 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 command_exists apt; then sudo apt install -y unzip @@ -132,7 +220,12 @@ if ! command_exists unzip; then fi 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 " Java ${JAVA_VERSION} @ ${JAVA_HOME:-$(java_home_from_path)}" echo "" # Verify we're in the right directory @@ -145,10 +238,13 @@ fi chmod +x ./gradlew 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" else echo -e "${RED}✗${NC} Gradle wrapper failed" + echo "$GRADLE_OUT" exit 1 fi