Skip to content

Commit d75ccd0

Browse files
Merge pull request #1817 from codeflash-ai/feat/mvn-central-runtime-resolution
feat: resolve codeflash-runtime from Maven Central
2 parents f3e36fe + 66e8060 commit d75ccd0

3 files changed

Lines changed: 86 additions & 82 deletions

File tree

codeflash/languages/java/build_tools.py

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import re
1212
import shutil
1313
import subprocess
14+
import urllib.request
1415
import xml.etree.ElementTree as ET
1516
from dataclasses import dataclass
1617
from enum import Enum
@@ -24,49 +25,69 @@
2425
JACOCO_PLUGIN_VERSION = "0.8.13"
2526

2627

27-
# MVN_CENTRAL_TODO: Uncomment once codeflash-runtime is published to Maven Central.
28-
# Steps:
29-
# 1. Uncomment resolve_from_maven_central() below
30-
# 2. Uncomment the call in test_runner.py _ensure_codeflash_runtime()
31-
# 3. Exclude the JAR from the PyPI wheel (see MVN_CENTRAL_TODO in pyproject.toml)
32-
# 4. The install:install-file fallback in _ensure_codeflash_runtime() can be removed
33-
# 5. The bundled JAR in resources/ can be removed from the repo
34-
#
35-
# Alternative: Instead of Maven resolution, you can download the JAR from GitHub
36-
# Releases using a download_jar_to_cache() helper — see git history for the
37-
# implementation that was removed in this cleanup.
38-
#
39-
# def resolve_from_maven_central(maven_root: Path) -> bool:
40-
# """Ask Maven to resolve codeflash-runtime from Maven Central.
41-
#
42-
# This downloads the JAR to ~/.m2/repository/ automatically.
43-
# Only works once the JAR is published to Maven Central.
44-
#
45-
# Returns True if Maven successfully resolved the artifact.
46-
# """
47-
# mvn = find_maven_executable()
48-
# if not mvn:
49-
# return False
50-
# cmd = [
51-
# mvn,
52-
# "dependency:resolve",
53-
# f"-Dartifact=com.codeflash:codeflash-runtime:{CODEFLASH_RUNTIME_VERSION}",
54-
# "-B",
55-
# "-q",
56-
# ]
57-
# try:
58-
# result = subprocess.run(
59-
# cmd, check=False, cwd=maven_root, capture_output=True, text=True, timeout=60
60-
# )
61-
# if result.returncode == 0:
62-
# logger.info("Resolved codeflash-runtime %s from Maven Central", CODEFLASH_RUNTIME_VERSION)
63-
# return True
64-
# logger.debug("Maven Central resolution failed: %s", result.stderr)
65-
# return False
66-
# except Exception as e:
67-
# logger.debug("Maven Central resolution error: %s", e)
68-
# return False
69-
# --------------------------------------------------------------------------
28+
GITHUB_RELEASE_URL = (
29+
"https://github.com/codeflash-ai/codeflash/releases/download"
30+
f"/runtime-v{CODEFLASH_RUNTIME_VERSION}/{CODEFLASH_RUNTIME_JAR_NAME}"
31+
)
32+
33+
CODEFLASH_CACHE_DIR = Path.home() / ".cache" / "codeflash"
34+
35+
36+
def download_from_github_releases() -> Path | None:
37+
"""Download codeflash-runtime JAR from GitHub Releases.
38+
39+
Downloads to ~/.cache/codeflash/ and returns the path to the downloaded JAR.
40+
Returns None if the download fails (e.g., no release published yet, network error).
41+
42+
This serves as a fallback when Maven Central resolution fails — for example,
43+
when the user's project doesn't have Maven installed or Maven Central is unreachable.
44+
Requires a GitHub Release tagged 'runtime-v{version}' with the JAR as an asset.
45+
"""
46+
cache_jar = CODEFLASH_CACHE_DIR / CODEFLASH_RUNTIME_JAR_NAME
47+
if cache_jar.exists():
48+
logger.info("Found cached codeflash-runtime JAR: %s", cache_jar)
49+
return cache_jar
50+
51+
try:
52+
CODEFLASH_CACHE_DIR.mkdir(parents=True, exist_ok=True)
53+
logger.info("Downloading codeflash-runtime from GitHub Releases: %s", GITHUB_RELEASE_URL)
54+
urllib.request.urlretrieve(GITHUB_RELEASE_URL, cache_jar) # noqa: S310
55+
logger.info("Downloaded codeflash-runtime to %s", cache_jar)
56+
return cache_jar
57+
except Exception as e:
58+
logger.debug("GitHub Releases download failed: %s", e)
59+
cache_jar.unlink(missing_ok=True)
60+
return None
61+
62+
63+
def resolve_from_maven_central(maven_root: Path) -> bool:
64+
"""Ask Maven to resolve codeflash-runtime from Maven Central.
65+
66+
This downloads the JAR to ~/.m2/repository/ automatically.
67+
Only works once the JAR is published to Maven Central.
68+
69+
Returns True if Maven successfully resolved the artifact.
70+
"""
71+
mvn = find_maven_executable()
72+
if not mvn:
73+
return False
74+
cmd = [
75+
mvn,
76+
"dependency:resolve",
77+
f"-Dartifact=com.codeflash:codeflash-runtime:{CODEFLASH_RUNTIME_VERSION}",
78+
"-B",
79+
"-q",
80+
]
81+
try:
82+
result = subprocess.run(cmd, check=False, cwd=maven_root, capture_output=True, text=True, timeout=60)
83+
if result.returncode == 0:
84+
logger.info("Resolved codeflash-runtime %s from Maven Central", CODEFLASH_RUNTIME_VERSION)
85+
return True
86+
logger.debug("Maven Central resolution failed: %s", result.stderr)
87+
return False
88+
except Exception as e:
89+
logger.debug("Maven Central resolution error: %s", e)
90+
return False
7091

7192

7293
def _safe_parse_xml(file_path: Path) -> ET.ElementTree:

codeflash/languages/java/test_runner.py

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
CODEFLASH_RUNTIME_JAR_NAME,
2828
CODEFLASH_RUNTIME_VERSION,
2929
add_codeflash_dependency_to_pom,
30+
download_from_github_releases,
3031
find_maven_executable,
3132
get_jacoco_xml_path,
3233
install_codeflash_runtime,
3334
is_jacoco_configured,
35+
resolve_from_maven_central,
3436
)
3537

3638
_MAVEN_NS = "http://maven.apache.org/POM/4.0.0"
@@ -232,16 +234,12 @@ def generate_jacoco_report(exec_file: Path, classfiles_dir: Path, sourcefiles_di
232234
return False
233235

234236

235-
# MVN_CENTRAL_TODO: Once codeflash-runtime is published to Maven Central, Maven will
236-
# resolve the JAR automatically via the pom.xml <dependency>. At that point step 2
237-
# (bundled resources/) can be removed and the JAR excluded from the PyPI wheel.
238237
def _find_runtime_jar() -> Path | None:
239-
"""Find the codeflash-runtime JAR file.
238+
"""Find the codeflash-runtime JAR file locally.
240239
241240
Resolution order:
242-
1. Local Maven cache (~/.m2) — fastest, already resolved by Maven or previous install
243-
2. Bundled in Python package (resources/) — available after pip install codeflash
244-
3. Development build directory — only when running from source checkout
241+
1. Local Maven cache (~/.m2) — fastest, already resolved by Maven Central or previous install
242+
2. Development build directory — only when running from source checkout
245243
"""
246244
# 1. Check local Maven repository (fastest — already installed by Maven or install:install-file)
247245
m2_jar = (
@@ -257,12 +255,7 @@ def _find_runtime_jar() -> Path | None:
257255
if m2_jar.exists():
258256
return m2_jar
259257

260-
# 2. Check bundled JAR in package resources (available when JAR is shipped with pip package)
261-
resources_jar = Path(__file__).parent / "resources" / CODEFLASH_RUNTIME_JAR_NAME
262-
if resources_jar.exists():
263-
return resources_jar
264-
265-
# 3. Check development build directory (only when running from source checkout)
258+
# 2. Check development build directory (only when running from source checkout)
266259
dev_jar = (
267260
Path(__file__).parent.parent.parent.parent / "codeflash-java-runtime" / "target" / CODEFLASH_RUNTIME_JAR_NAME
268261
)
@@ -291,24 +284,7 @@ def _ensure_codeflash_runtime(maven_root: Path, test_module: str | None) -> bool
291284
if cache_key in _runtime_ensured:
292285
return _runtime_ensured[cache_key]
293286

294-
runtime_jar = _find_runtime_jar()
295-
if runtime_jar is None:
296-
logger.error("codeflash-runtime JAR not found. Generated tests will fail to compile.")
297-
return False
298-
299-
# Install to local Maven repo if not already there.
300-
#
301-
# MVN_CENTRAL_TODO: Once codeflash-runtime is published to Maven Central, uncomment
302-
# the block below and remove the install:install-file fallback beneath it. Maven will
303-
# download the JAR from Central automatically when it sees the <dependency> in pom.xml.
304-
#
305-
# from codeflash.languages.java.build_tools import resolve_from_maven_central
306-
# if resolve_from_maven_central(maven_root):
307-
# pass # Maven resolved to ~/.m2 — no manual install needed
308-
# else:
309-
# if not install_codeflash_runtime(maven_root, runtime_jar):
310-
# logger.error("Failed to install codeflash-runtime to local Maven repository")
311-
# return False
287+
# Ensure codeflash-runtime is in the local Maven repository.
312288
m2_jar = (
313289
Path.home()
314290
/ ".m2"
@@ -320,10 +296,20 @@ def _ensure_codeflash_runtime(maven_root: Path, test_module: str | None) -> bool
320296
/ CODEFLASH_RUNTIME_JAR_NAME
321297
)
322298
if not m2_jar.exists():
323-
logger.info("Installing codeflash-runtime JAR to local Maven repository")
324-
if not install_codeflash_runtime(maven_root, runtime_jar):
325-
logger.error("Failed to install codeflash-runtime to local Maven repository")
326-
return False
299+
# Try resolving from Maven Central first
300+
if not resolve_from_maven_central(maven_root):
301+
# Fallback: download from GitHub Releases (works when Maven Central is unreachable)
302+
runtime_jar = download_from_github_releases()
303+
if runtime_jar is None:
304+
logger.error(
305+
"codeflash-runtime JAR not found. Maven Central resolution failed and "
306+
"GitHub Releases download failed. Generated tests will fail to compile."
307+
)
308+
return False
309+
logger.info("Installing codeflash-runtime JAR to local Maven repository from %s", runtime_jar)
310+
if not install_codeflash_runtime(maven_root, runtime_jar):
311+
logger.error("Failed to install codeflash-runtime to local Maven repository")
312+
return False
327313

328314
# Add dependency to the appropriate pom.xml
329315
if test_module:

pyproject.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,7 @@ exclude = [
196196
"Thumbs.db",
197197
"venv",
198198
"env",
199-
# MVN_CENTRAL_TODO: Once codeflash-runtime is available on Maven Central (or GitHub
200-
# Releases), exclude it from the wheel to save ~15MB. Users will get it via Maven
201-
# resolution or download-on-first-run. Uncomment the line below:
202-
# "codeflash/languages/java/resources/codeflash-runtime-*.jar",
199+
"codeflash/languages/java/resources/codeflash-runtime-*.jar",
203200
]
204201

205202
[tool.mypy]

0 commit comments

Comments
 (0)