From 54a14765ee37d26a196e240ec965d4a955093cd3 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Fri, 10 Apr 2026 21:47:15 +0000 Subject: [PATCH 1/2] fix: reject JDK < 11 early with clear error message When running with JDK 8, the pipeline would fail late with a cryptic UnsupportedClassVersionError from the runtime JAR. Now checks the detected Java version in ensure_runtime_environment() and returns False with a clear error message explaining that JDK 11+ is required. Co-Authored-By: Claude Opus 4.6 --- codeflash/languages/java/support.py | 15 +++++++ .../test_languages/test_java/test_support.py | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/codeflash/languages/java/support.py b/codeflash/languages/java/support.py index 7115d2225..55af834f6 100644 --- a/codeflash/languages/java/support.py +++ b/codeflash/languages/java/support.py @@ -546,6 +546,21 @@ def ensure_runtime_environment(self, project_root: Path) -> bool: if self._language_version is None: self._detect_java_version() + if self._language_version is not None: + try: + major = int(self._language_version) + if major < 11: + logger.error( + "Java %s detected, but codeflash requires JDK 11 or later. " + "The codeflash-runtime JAR and --add-opens flags are incompatible with JDK %s. " + "Please install JDK 11+ and ensure it is on your PATH.", + self._language_version, + self._language_version, + ) + return False + except ValueError: + pass + self._test_framework = config.test_framework return True diff --git a/tests/test_languages/test_java/test_support.py b/tests/test_languages/test_java/test_support.py index b5cba5ab4..de48d68d9 100644 --- a/tests/test_languages/test_java/test_support.py +++ b/tests/test_languages/test_java/test_support.py @@ -134,3 +134,44 @@ def test_discover_functions_from_fixture(self, support, java_fixture_path: Path) source = calculator_file.read_text(encoding="utf-8") functions = support.discover_functions(source, calculator_file) assert len(functions) > 0 + + +class TestJdkVersionCheck: + def test_jdk8_rejected(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "8" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is False + + def test_jdk11_accepted(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "11" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is True + + def test_jdk21_accepted(self, tmp_path: Path) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + + mock_config = MagicMock() + mock_config.java_version = "21" + mock_config.test_framework = "junit5" + + with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): + result = support.ensure_runtime_environment(tmp_path) + assert result is True From c40b5e7a74be0aadd48d142f6f2739c25e2105e0 Mon Sep 17 00:00:00 2001 From: Mohamed Ashraf Date: Tue, 21 Apr 2026 02:05:33 +0000 Subject: [PATCH 2/2] refactor: drop legacy JDK 8 version mapping in _detect_java_version --- codeflash/languages/java/support.py | 6 ++-- .../test_languages/test_java/test_support.py | 30 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/codeflash/languages/java/support.py b/codeflash/languages/java/support.py index 55af834f6..500308391 100644 --- a/codeflash/languages/java/support.py +++ b/codeflash/languages/java/support.py @@ -583,9 +583,9 @@ def _detect_java_version(self) -> None: end = line.find('"', start + 1) if start != -1 and end != -1: full_version = line[start + 1 : end] - # Use major version only: "17.0.2" -> "17", "1.8.0_292" -> "8" - major = full_version.split(".")[0] - self._language_version = "8" if major == "1" else major + # Use major version only: "17.0.2" -> "17". JDK 8 and earlier (reported as + # "1.x.y") are unsupported — the downstream minimum-version check rejects them. + self._language_version = full_version.split(".")[0] return except Exception: pass diff --git a/tests/test_languages/test_java/test_support.py b/tests/test_languages/test_java/test_support.py index de48d68d9..3f991f7a4 100644 --- a/tests/test_languages/test_java/test_support.py +++ b/tests/test_languages/test_java/test_support.py @@ -175,3 +175,33 @@ def test_jdk21_accepted(self, tmp_path: Path) -> None: with patch("codeflash.languages.java.support.detect_java_project", return_value=mock_config): result = support.ensure_runtime_environment(tmp_path) assert result is True + + def test_detect_java_version_legacy_jdk8_format(self) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + support._language_version = None + + mock_result = MagicMock() + mock_result.stderr = 'openjdk version "1.8.0_292"\n' + mock_result.stdout = "" + + with patch("subprocess.run", return_value=mock_result): + support._detect_java_version() + + assert support._language_version == "1" + + def test_detect_java_version_modern_format(self) -> None: + from unittest.mock import MagicMock, patch + + support = get_java_support() + support._language_version = None + + mock_result = MagicMock() + mock_result.stderr = 'openjdk version "17.0.2"\n' + mock_result.stdout = "" + + with patch("subprocess.run", return_value=mock_result): + support._detect_java_version() + + assert support._language_version == "17"