From 043be016f3d92cbef1b18ca4672ffbf23876682f Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Mar 2026 14:16:13 +0300 Subject: [PATCH 1/2] Replace DISCO API with GitHub API for Mandrel latest version resolution. The Foojay DISCO API is no longer needed for resolving the latest Mandrel release. Instead, use the GitHub Releases API to search for matching assets directly in the graalvm/mandrel repository. Co-Authored-By: Claude Opus 4.6 --- __tests__/mandrel.test.ts | 2 +- dist/main.js | 46 +++++++++++++++++++-------------------- src/mandrel.ts | 45 ++++++-------------------------------- src/utils.ts | 21 ++++++++++++++++++ 4 files changed, 51 insertions(+), 63 deletions(-) diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts index 1c863c65..1a827ade 100644 --- a/__tests__/mandrel.test.ts +++ b/__tests__/mandrel.test.ts @@ -59,7 +59,7 @@ test('find latest', async () => { test('get known latest Mandrel for specific JDK', async () => { // Test deprecated versions that won't get updates anymore for (const combination of [ - ['11', '22.2.0.0-Final'], + ['11', '21.3.6.0-Final'], ['20', '23.0.1.2-Final'] ]) { const latest = await mandrel.getLatestMandrelReleaseUrl(combination[0]) diff --git a/dist/main.js b/dist/main.js index d8269df3..97c30db4 100644 --- a/dist/main.js +++ b/dist/main.js @@ -38095,6 +38095,23 @@ async function getLatestRelease(repo) { repo })).data; /** missing digest property */ } +async function findLatestReleaseWithAsset(repo, assetNamePredicate) { + const octokit = getOctokit(); + const iterator = octokit.paginate.iterator(octokit.rest.repos.listReleases, { + owner: GRAALVM_GH_USER, + repo, + per_page: 100 + }); + for await (const { data: releases } of iterator) { + for (const release of releases) { + const matchingAsset = release.assets.find((a) => assetNamePredicate(a.name)); + if (matchingAsset) { + return matchingAsset.browser_download_url; + } + } + } + throw new Error(`Could not find a release in '${repo}' with a matching asset.`); +} async function getContents(repo, path) { const octokit = getOctokit(); return (await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { @@ -81938,9 +81955,9 @@ async function installGUComponents(gdsToken, graalVMHome, components) { } } +const MANDREL_REPO = 'mandrel'; const MANDREL_TAG_PREFIX = MANDREL_NAMESPACE; const MANDREL_DL_BASE = 'https://github.com/graalvm/mandrel/releases/download'; -const DISCO_API_BASE = 'https://api.foojay.io/disco/v3.0/packages/jdks'; async function setUpMandrel(mandrelVersion, javaVersion) { const version = stripMandrelNamespace(mandrelVersion); let mandrelHome; @@ -81976,32 +81993,13 @@ function getTagFromURI(uri) { } } async function getLatestMandrelReleaseUrl(javaVersion) { - const url = `${DISCO_API_BASE}?jdk_version=${javaVersion}&distribution=${DISTRIBUTION_MANDREL}&architecture=${JDK_ARCH}&operating_system=${JDK_PLATFORM}&latest=per_distro`; - const _http = new HttpClient(); - const response = await _http.getJson(url); - if (response.statusCode !== 200) { - throw new Error(`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.result}`); - } - const result = response.result?.result[0]; - try { - const pkg_info_uri = result.links.pkg_info_uri; - return await getLatestMandrelReleaseUrlHelper(_http, javaVersion, pkg_info_uri); - } - catch (error) { - throw new Error(`Failed to get latest Mandrel release for Java ${javaVersion} from DISCO API: ${error}`); - } -} -async function getLatestMandrelReleaseUrlHelper(_http, java_version, pkg_info_uri) { - const response = await _http.getJson(pkg_info_uri); - if (response.statusCode !== 200) { - throw new Error(`Failed to fetch package info of latest Mandrel release for Java ${java_version} from DISCO API: ${response.result}`); - } - const result = response.result?.result[0]; + const expectedPrefix = `mandrel-java${javaVersion}-${GRAALVM_PLATFORM}-${GRAALVM_ARCH}-`; + const expectedSuffix = GRAALVM_FILE_EXTENSION; try { - return result.direct_download_uri; + return await findLatestReleaseWithAsset(MANDREL_REPO, (name) => name.startsWith(expectedPrefix) && name.endsWith(expectedSuffix)); } catch (error) { - throw new Error(`Failed to get download URI of latest Mandrel release for Java ${java_version} from DISCO API: ${error}`); + throw new Error(`Failed to find latest Mandrel release for Java ${javaVersion}. Are you sure java-version: '${javaVersion}' is correct? ${error}`); } } async function setUpMandrelRelease(version, javaVersion) { diff --git a/src/mandrel.ts b/src/mandrel.ts index e6e03015..4736bc0d 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -1,20 +1,11 @@ import * as c from './constants.js' -import * as httpClient from '@actions/http-client' -import { downloadExtractAndCacheJDK } from './utils.js' +import { downloadExtractAndCacheJDK, findLatestReleaseWithAsset } from './utils.js' import { downloadTool } from '@actions/tool-cache' import { basename } from 'path' export const MANDREL_REPO = 'mandrel' export const MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE const MANDREL_DL_BASE = 'https://github.com/graalvm/mandrel/releases/download' -const DISCO_API_BASE = 'https://api.foojay.io/disco/v3.0/packages/jdks' - -interface JdkData { - message: string - /* eslint-disable @typescript-eslint/no-explicit-any */ - result: any - /* eslint-enable @typescript-eslint/no-explicit-any */ -} export async function setUpMandrel(mandrelVersion: string, javaVersion: string): Promise { const version = stripMandrelNamespace(mandrelVersion) @@ -55,38 +46,16 @@ function getTagFromURI(uri: string): string { } export async function getLatestMandrelReleaseUrl(javaVersion: string): Promise { - const url = `${DISCO_API_BASE}?jdk_version=${javaVersion}&distribution=${c.DISTRIBUTION_MANDREL}&architecture=${c.JDK_ARCH}&operating_system=${c.JDK_PLATFORM}&latest=per_distro` - const _http = new httpClient.HttpClient() - const response = await _http.getJson(url) - if (response.statusCode !== 200) { - throw new Error(`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.result}`) - } - const result = response.result?.result[0] + const expectedPrefix = `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}-${c.GRAALVM_ARCH}-` + const expectedSuffix = c.GRAALVM_FILE_EXTENSION try { - const pkg_info_uri = result.links.pkg_info_uri - return await getLatestMandrelReleaseUrlHelper(_http, javaVersion, pkg_info_uri) - } catch (error) { - throw new Error(`Failed to get latest Mandrel release for Java ${javaVersion} from DISCO API: ${error}`) - } -} - -async function getLatestMandrelReleaseUrlHelper( - _http: httpClient.HttpClient, - java_version: string, - pkg_info_uri: string -): Promise { - const response = await _http.getJson(pkg_info_uri) - if (response.statusCode !== 200) { - throw new Error( - `Failed to fetch package info of latest Mandrel release for Java ${java_version} from DISCO API: ${response.result}` + return await findLatestReleaseWithAsset( + MANDREL_REPO, + (name) => name.startsWith(expectedPrefix) && name.endsWith(expectedSuffix) ) - } - const result = response.result?.result[0] - try { - return result.direct_download_uri } catch (error) { throw new Error( - `Failed to get download URI of latest Mandrel release for Java ${java_version} from DISCO API: ${error}` + `Failed to find latest Mandrel release for Java ${javaVersion}. Are you sure java-version: '${javaVersion}' is correct? ${error}` ) } } diff --git a/src/utils.ts b/src/utils.ts index 4d489d33..8ace6e04 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -29,6 +29,27 @@ export async function getLatestRelease(repo: string): Promise boolean +): Promise { + const octokit = getOctokit() + const iterator = octokit.paginate.iterator(octokit.rest.repos.listReleases, { + owner: c.GRAALVM_GH_USER, + repo, + per_page: 100 + }) + for await (const { data: releases } of iterator) { + for (const release of releases) { + const matchingAsset = release.assets.find((a) => assetNamePredicate(a.name)) + if (matchingAsset) { + return matchingAsset.browser_download_url + } + } + } + throw new Error(`Could not find a release in '${repo}' with a matching asset.`) +} + export async function getContents(repo: string, path: string): Promise { const octokit = getOctokit() return ( From 239690ee76f8858220d999461a532a186b97da84 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Mar 2026 18:38:04 +0300 Subject: [PATCH 2/2] Fix macOS platform mismatch in Mandrel asset matching and add test. Use `JDK_PLATFORM` instead of `GRAALVM_PLATFORM` so macOS resolves to `macos` (not `darwin`) when matching Mandrel release assets. Extract `matchesMandrelAsset()` helper and add a unit test covering all platforms. Co-Authored-By: Claude Opus 4.6 --- __tests__/mandrel.test.ts | 25 +++++++++++++++++++++++++ dist/main.js | 8 +++++--- src/mandrel.ts | 18 +++++++++++++----- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts index 1a827ade..e47e459b 100644 --- a/__tests__/mandrel.test.ts +++ b/__tests__/mandrel.test.ts @@ -75,3 +75,28 @@ test('get latest Mandrel for specific JDK', async () => { expect(latest).toContain(`mandrel-java${javaVersion}`) } }) + +test('matchesMandrelAsset matches correct platform-specific asset names', () => { + // Real asset names from mandrel-23.1.10.0-Final release + const linuxAmd64 = 'mandrel-java21-linux-amd64-23.1.10.0-Final.tar.gz' + const linuxAarch64 = 'mandrel-java21-linux-aarch64-23.1.10.0-Final.tar.gz' + const macosAarch64 = 'mandrel-java21-macos-aarch64-23.1.10.0-Final.tar.gz' + const windowsAmd64 = 'mandrel-java21-windows-amd64-23.1.10.0-Final.zip' + + // Linux x64 + expect(mandrel.matchesMandrelAsset(linuxAmd64, '21', 'linux', 'amd64', '.tar.gz')).toBe(true) + expect(mandrel.matchesMandrelAsset(linuxAarch64, '21', 'linux', 'amd64', '.tar.gz')).toBe(false) + + // macOS uses 'macos', not 'darwin' + expect(mandrel.matchesMandrelAsset(macosAarch64, '21', 'macos', 'aarch64', '.tar.gz')).toBe(true) + expect(mandrel.matchesMandrelAsset(macosAarch64, '21', 'darwin', 'aarch64', '.tar.gz')).toBe(false) + + // Windows + expect(mandrel.matchesMandrelAsset(windowsAmd64, '21', 'windows', 'amd64', '.zip')).toBe(true) + + // Wrong java version + expect(mandrel.matchesMandrelAsset(linuxAmd64, '17', 'linux', 'amd64', '.tar.gz')).toBe(false) + + // Wrong extension + expect(mandrel.matchesMandrelAsset(linuxAmd64, '21', 'linux', 'amd64', '.zip')).toBe(false) +}) diff --git a/dist/main.js b/dist/main.js index 97c30db4..d05446c4 100644 --- a/dist/main.js +++ b/dist/main.js @@ -81992,11 +81992,13 @@ function getTagFromURI(uri) { throw new Error(`Failed to extract tag from URI ${uri}: ${error}`); } } +function matchesMandrelAsset(name, javaVersion, platform, arch, extension) { + const expectedPrefix = `mandrel-java${javaVersion}-${platform}-${arch}-`; + return name.startsWith(expectedPrefix) && name.endsWith(extension); +} async function getLatestMandrelReleaseUrl(javaVersion) { - const expectedPrefix = `mandrel-java${javaVersion}-${GRAALVM_PLATFORM}-${GRAALVM_ARCH}-`; - const expectedSuffix = GRAALVM_FILE_EXTENSION; try { - return await findLatestReleaseWithAsset(MANDREL_REPO, (name) => name.startsWith(expectedPrefix) && name.endsWith(expectedSuffix)); + return await findLatestReleaseWithAsset(MANDREL_REPO, (name) => matchesMandrelAsset(name, javaVersion, JDK_PLATFORM, GRAALVM_ARCH, GRAALVM_FILE_EXTENSION)); } catch (error) { throw new Error(`Failed to find latest Mandrel release for Java ${javaVersion}. Are you sure java-version: '${javaVersion}' is correct? ${error}`); diff --git a/src/mandrel.ts b/src/mandrel.ts index 4736bc0d..c8486e04 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -45,13 +45,21 @@ function getTagFromURI(uri: string): string { } } +export function matchesMandrelAsset( + name: string, + javaVersion: string, + platform: string, + arch: string, + extension: string +): boolean { + const expectedPrefix = `mandrel-java${javaVersion}-${platform}-${arch}-` + return name.startsWith(expectedPrefix) && name.endsWith(extension) +} + export async function getLatestMandrelReleaseUrl(javaVersion: string): Promise { - const expectedPrefix = `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}-${c.GRAALVM_ARCH}-` - const expectedSuffix = c.GRAALVM_FILE_EXTENSION try { - return await findLatestReleaseWithAsset( - MANDREL_REPO, - (name) => name.startsWith(expectedPrefix) && name.endsWith(expectedSuffix) + return await findLatestReleaseWithAsset(MANDREL_REPO, (name) => + matchesMandrelAsset(name, javaVersion, c.JDK_PLATFORM, c.GRAALVM_ARCH, c.GRAALVM_FILE_EXTENSION) ) } catch (error) { throw new Error(