|
36 | 36 | from __future__ import annotations |
37 | 37 |
|
38 | 38 | import hashlib |
39 | | -import json |
40 | 39 | import logging |
41 | 40 | import os |
42 | 41 | import platform |
43 | 42 | import stat |
44 | 43 | import tarfile |
| 44 | +import urllib.error |
| 45 | +import urllib.parse |
45 | 46 | import urllib.request |
46 | 47 | import zipfile |
47 | 48 | from pathlib import Path |
@@ -70,20 +71,36 @@ def _os_arch(cls) -> tuple[str, str]: |
70 | 71 |
|
71 | 72 | @classmethod |
72 | 73 | def _resolve_asset(cls) -> tuple[str, str]: |
73 | | - """Return ``(download_url, sha256)`` for the pinned JDK binary.""" |
| 74 | + """Return ``(download_url, sha256)`` for the pinned JDK binary. |
| 75 | +
|
| 76 | + Resolves via the Adoptium ``/binary/version`` endpoint, which 307-redirects to the |
| 77 | + GitHub release asset; the checksum comes from the asset's adjacent ``.sha256.txt``. The |
| 78 | + older ``/assets/version/{release}`` query endpoint is not used: it returns 404 for pinned |
| 79 | + releases (e.g. ``jdk-21.0.5+11``), even though the release exists. |
| 80 | + """ |
74 | 81 | os_, arch = cls._os_arch() |
75 | | - url = ( |
76 | | - f"{cls._API}/assets/version/{JDK_RELEASE}" |
77 | | - f"?os={os_}&architecture={arch}&image_type=jdk" |
78 | | - f"&jvm_impl=hotspot&heap_size=normal&vendor=eclipse" |
79 | | - ) |
80 | | - req = urllib.request.Request(url, headers={"User-Agent": "cldk"}) |
81 | | - with urllib.request.urlopen(req, timeout=30) as resp: |
82 | | - data = json.load(resp) |
83 | | - if not data: |
84 | | - raise RuntimeError(f"No Temurin {JDK_RELEASE} build for {os_}/{arch}") |
85 | | - pkg = data[0]["binaries"][0]["package"] |
86 | | - return pkg["link"], pkg["checksum"] |
| 82 | + release = urllib.parse.quote(JDK_RELEASE, safe="") # encode the '+' in the path |
| 83 | + binary_url = f"{cls._API}/binary/version/{release}/{os_}/{arch}/jdk/hotspot/normal/eclipse" |
| 84 | + |
| 85 | + # Capture the redirect target (the GitHub asset URL) without downloading the binary. |
| 86 | + class _NoRedirect(urllib.request.HTTPRedirectHandler): |
| 87 | + def redirect_request(self, *args, **kwargs): |
| 88 | + return None |
| 89 | + |
| 90 | + opener = urllib.request.build_opener(_NoRedirect) |
| 91 | + req = urllib.request.Request(binary_url, headers={"User-Agent": "cldk"}) |
| 92 | + try: |
| 93 | + opener.open(req, timeout=30) |
| 94 | + raise RuntimeError(f"Expected a redirect to the Temurin {JDK_RELEASE} asset from {binary_url}") |
| 95 | + except urllib.error.HTTPError as exc: |
| 96 | + if exc.code not in (301, 302, 303, 307, 308) or not exc.headers.get("Location"): |
| 97 | + raise RuntimeError(f"No Temurin {JDK_RELEASE} build for {os_}/{arch} (HTTP {exc.code})") from exc |
| 98 | + asset_url = exc.headers["Location"] |
| 99 | + |
| 100 | + sha_req = urllib.request.Request(asset_url + ".sha256.txt", headers={"User-Agent": "cldk"}) |
| 101 | + with urllib.request.urlopen(sha_req, timeout=30) as resp: |
| 102 | + sha = resp.read().decode().split()[0] |
| 103 | + return asset_url, sha |
87 | 104 |
|
88 | 105 | @classmethod |
89 | 106 | def _java_home(cls, root: Path) -> Path: |
|
0 commit comments