Resolving existing gradle distributions, aka built-in wrapper

This commit is contained in:
MaXFeeD
2025-05-21 20:47:43 +03:00
parent 8103d1ec6b
commit 27534a8294
+92 -11
View File
@@ -1,20 +1,24 @@
import re
from hashlib import md5
from os import environ
from os.path import join, splitext
from typing import NamedTuple
from hashlib import md5, sha256
from os import environ, walk
from os.path import basename, dirname, isdir, isfile, join, splitext
from typing import NamedTuple, Optional
from .utils import encode_int
from .fetch import queue_download_request, retrieve_bytes
from .output_directory import FileLock
from .utils import AttributeZipFile, encode_int, ensure_directory, remove_tree
# (numeric part, major version, minor version), (stage part, stage, patch number), (commit part, commit/date, timezone)
GRADLE_VERSION_REGEX = re.compile(r"((\d+)(\.\d+)+)(-([^\W\d_]+)-(\w+))?(-(SNAPSHOT|\d{14}([-+]\d{4})?))?")
COMPARABLE_VERSION_REGEX = re.compile(r"(\d+)(\.\d+)")
class GradleVersion(NamedTuple):
version: str
comparable: float
major: int
minor: int
snapshot: bool
def get_gradle_home() -> str:
def find_gradle_home() -> str:
gradle_home = environ.get("GRADLE_USER_HOME")
if gradle_home is None:
from .output_directory import get_user_directory
@@ -23,15 +27,16 @@ def get_gradle_home() -> str:
def parse_gradle_specification(version: str) -> GradleVersion:
if version == "latest" or version == "nightly" or version == "release-nightly" or version == "release-candidate":
return GradleVersion(version, 0.0, version != "latest" and version != "release-candidate")
return GradleVersion(version, 0, 0, version != "latest" and version != "release-candidate")
spec = GRADLE_VERSION_REGEX.fullmatch(version)
if spec is None:
raise ValueError(f"Specify a valid Gradle release listed on https://gradle.org/releases/. Got: {version!r}.")
comparable_spec = COMPARABLE_VERSION_REGEX.match(spec.group(1))
assert comparable_spec
comparable = float(spec.group(2) + spec.group(3))
snapshot = spec.group(5) in ("snapshot", "commit") or spec.group(8) is not None
return GradleVersion(spec.string, comparable, snapshot)
return GradleVersion(spec.string, int(comparable_spec.group(1)), int(comparable_spec.group(2)), snapshot)
def get_gradle_distribution_url(spec: GradleVersion, type: str = "bin") -> str:
repository = "distributions" if not spec.snapshot else "distributions-snapshots"
@@ -52,4 +57,80 @@ def url_to_local_gradle_distribution(url: str) -> str:
archive_name = splitext(archive_path)[0]
url_checksum = url_as_gradle_checksum(url)
return join("wrapper", "dists", archive_name, url_checksum, archive_path)
return join(archive_name, url_checksum, archive_path)
def get_user_gradle_disribution(relative_path: str) -> str:
return join(find_gradle_home(), "wrapper", "dists", relative_path)
def verify_gradle_installation(archive_path: str, without_marker: bool = False) -> Optional[str]:
distribution_directory = dirname(archive_path)
marker_file = join(distribution_directory, f"{basename(archive_path)}.ok")
if not isdir(distribution_directory) or (not without_marker and not isfile(marker_file)):
return None
for dirpath, dirnames, filenames in walk(distribution_directory, followlinks=True):
for relative_path in dirnames:
gradle_home = join(dirpath, relative_path)
if find_gradle_launcher(gradle_home):
return gradle_home
break
def find_gradle_launcher(gradle_home: str) -> Optional[str]:
executable_directory = join(gradle_home, "lib")
if not isdir(executable_directory):
return None
for dirpath, dirnames, filenames in walk(executable_directory, followlinks=True):
for relative_path in filenames:
if relative_path[:16] == "gradle-launcher-" and relative_path[-4:] == ".jar":
return join(dirpath, relative_path)
def verify_gradle_remote_checksum(archive_path: str, url: str) -> bool:
if not isfile(archive_path):
return False
local_hash = sha256()
with open(archive_path, "rb") as file:
while True:
chunk = file.read(4096)
if not chunk:
break
local_hash.update(chunk)
remote_hash = str(retrieve_bytes(url), encoding="ascii")
return remote_hash == local_hash.hexdigest()
def download_gradle_version(archive_path: str, url: str) -> str:
distribution_directory = dirname(archive_path)
ensure_directory(distribution_directory)
marker_file = join(distribution_directory, f"{basename(archive_path)}.ok")
remove_tree(marker_file)
for dirpath, dirnames, filenames in walk(distribution_directory, followlinks=True):
for relative_path in dirnames:
remove_tree(join(dirpath, relative_path))
if not verify_gradle_remote_checksum(archive_path, f"{url}.sha256"):
remove_tree(archive_path)
queue_download_request(url, output_path=archive_path)
if not verify_gradle_remote_checksum(archive_path, f"{url}.sha256"):
raise ValueError(f"SHA256 mismatch, failed to confirm validity of {basename(archive_path)} archive!")
with AttributeZipFile(archive_path, "r") as archive:
archive.extractall(distribution_directory)
gradle_home = verify_gradle_installation(archive_path, without_marker=True)
if not gradle_home:
raise ValueError(f"Unsupported distribution {basename(archive_path)}, please retry task with different version!")
open(marker_file, "x").close()
remove_tree(archive_path)
return gradle_home
def resolve_gradle_version(version: str) -> str:
gradle_specification = parse_gradle_specification(version)
distribution_url = get_gradle_distribution_url(gradle_specification)
local_path = url_to_local_gradle_distribution(distribution_url)
archive_path = get_user_gradle_disribution(local_path)
with FileLock(f"{archive_path}.lck", delete=False):
from time import sleep
sleep(2)
gradle_home = verify_gradle_installation(archive_path)
if not gradle_home:
gradle_home = download_gradle_version(archive_path, distribution_url)
return gradle_home