From c2fd2d6d8eca5b8d706930976934ab5cb8610dfd Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Oct 2023 15:12:37 +0200 Subject: [PATCH 1/9] Improve error log for wrong mandrel version and javaVersion comb. Add test to test similar to the GraalVM one for the expected error output. --- __tests__/mandrel.test.ts | 56 +++++++++++++++++++++++++++++++++++++++ src/mandrel.ts | 32 ++++++++++++++++++---- 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 __tests__/mandrel.test.ts diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts new file mode 100644 index 0000000..368eae9 --- /dev/null +++ b/__tests__/mandrel.test.ts @@ -0,0 +1,56 @@ +import * as path from 'path' +import * as mandrel from '../src/mandrel' +import {expect, test} from '@jest/globals' +import {getLatestRelease} from '../src/utils' + +process.env['RUNNER_TOOL_CACHE'] = path.join(__dirname, 'TOOL_CACHE') +process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP') + +test('request invalid version/javaVersion combination', async () => { + for (var combination of [ + ['mandrel-23.1.1.0-Final', '17'], + ['mandrel-23.0.2.1-Final', '21'], + ]) { + let error = new Error('unexpected') + try { + await mandrel.setUpMandrel(combination[0], combination[1]) + } catch (err) { + if (!(err instanceof Error)) { + fail(`Unexpected non-Error: ${err}`) + } + error = err + } + + expect(error).not.toBeUndefined() + expect(error.message).toContain('Failed to download') + expect(error.message).toContain('Are you sure version') + } +}) +test('request invalid version', async () => { + for (var combination of [ + ['mandrel-23.1.1.0', '21'], + ['mandrel-23.0.2.1', '17'], + ]) { + let error = new Error('unexpected') + try { + await mandrel.setUpMandrel(combination[0], combination[1]) + } catch (err) { + if (!(err instanceof Error)) { + fail(`Unexpected non-Error: ${err}`) + } + error = err + } + + expect(error).not.toBeUndefined() + expect(error.message).toContain('Failed to download') + expect(error.message).toContain('Are you sure version') + } +}) + +test('find latest', async () => { + // Make sure the action can find the latest Mandrel release + const latestRelease = await getLatestRelease(mandrel.MANDREL_REPO) + const tag_name = latestRelease.tag_name + expect(tag_name).toContain(mandrel.MANDREL_TAG_PREFIX) +}) + diff --git a/src/mandrel.ts b/src/mandrel.ts index 7c015f8..ebd39fc 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -1,9 +1,10 @@ import * as c from './constants' import {downloadExtractAndCacheJDK, getLatestRelease} from './utils' import {downloadTool} from '@actions/tool-cache' +import {basename} from 'path' -const MANDREL_REPO = 'mandrel' -const MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE +export const MANDREL_REPO = 'mandrel' +export const MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE const MANDREL_DL_BASE = 'https://github.com/graalvm/mandrel/releases/download' export async function setUpMandrel( @@ -45,16 +46,37 @@ async function setUpMandrelRelease( version: string, javaVersion: string ): Promise { - const identifier = determineMandrelIdentifier(version, javaVersion) - const downloadUrl = `${MANDREL_DL_BASE}/${MANDREL_TAG_PREFIX}${version}/${identifier}${c.GRAALVM_FILE_EXTENSION}` const toolName = determineToolName(javaVersion) return downloadExtractAndCacheJDK( - async () => downloadTool(downloadUrl), + async () => downloadMandrelJDK(version, javaVersion), toolName, version ) } +async function downloadMandrelJDK( + version: string, + javaVersion: string +): Promise { + const identifier = determineMandrelIdentifier(version, javaVersion) + const downloadUrl = `${MANDREL_DL_BASE}/${MANDREL_TAG_PREFIX}${version}/${identifier}${c.GRAALVM_FILE_EXTENSION}` + try { + return await downloadTool(downloadUrl) + } catch (error) { + if (error instanceof Error && error.message.includes('404')) { + // Not Found + throw new Error( + `Failed to download ${basename( + downloadUrl + )}. Are you sure version: '${version}' and java-version: '${javaVersion}' are correct?` + ) + } + throw new Error( + `Failed to download ${basename(downloadUrl)} (error: ${error}).` + ) + } +} + function determineMandrelIdentifier( version: string, javaVersion: string From 22b65d7de08873217a3191d4e3cf47f1f290678d Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Oct 2023 17:03:23 +0200 Subject: [PATCH 2/9] Use Foojay.io DISCO API to get latest Mandrel release --- __tests__/mandrel.test.ts | 16 +++++++++++ src/mandrel.ts | 58 +++++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts index 368eae9..8cb4b8e 100644 --- a/__tests__/mandrel.test.ts +++ b/__tests__/mandrel.test.ts @@ -54,3 +54,19 @@ test('find latest', async () => { expect(tag_name).toContain(mandrel.MANDREL_TAG_PREFIX) }) +test('get latest Mandrel for specific JDK', async () => { + // Test deprecated versions that won't get updates anymore + for (var combination of [ + ['11', '22.2.0.0-Final'], + ['20', '23.0.1.2-Final']]) { + const latest = await mandrel.getLatestMandrelReleaseUrl(combination[0]) + expect(latest).toContain(`mandrel-java${combination[0]}`) + expect(latest).toContain(combination[1]) + } + // Test supported versions + for (var javaVersion of ['17', '21']) { + const latest = await mandrel.getLatestMandrelReleaseUrl(javaVersion) + expect(latest).toContain(`mandrel-java${javaVersion}`) + } +}) + diff --git a/src/mandrel.ts b/src/mandrel.ts index ebd39fc..35b7f3d 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -6,6 +6,7 @@ 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' export async function setUpMandrel( graalvmVersion: string, @@ -30,16 +31,55 @@ export async function setUpMandrel( } async function setUpMandrelLatest(javaVersion: string): Promise { - const latestRelease = await getLatestRelease(MANDREL_REPO) - const tag_name = latestRelease.tag_name - if (tag_name.startsWith(MANDREL_TAG_PREFIX)) { - const latestVersion = tag_name.substring( - MANDREL_TAG_PREFIX.length, - tag_name.length - ) - return setUpMandrelRelease(latestVersion, javaVersion) + const latest_release_url = await getLatestMandrelReleaseUrl(javaVersion) + const version_tag = getTagFromURI(latest_release_url); + const version = version_tag.substring(c.MANDREL_NAMESPACE.length, version_tag.length) + console.log(version); + + const toolName = determineToolName(javaVersion) + return downloadExtractAndCacheJDK( + async () => downloadTool(latest_release_url), + toolName, + version + ) +} + +// Download URIs are of the form https://github.com/graalvm/mandrel/releases/download// +function getTagFromURI(uri: string): string { + const parts = uri.split('/'); + try { + return parts[parts.length - 2]; + } catch (error) { + throw new Error(`Failed to extract tag from URI ${uri}: ${error}`) + } +} + +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 response = await fetch(url) + if (!response.ok) { + throw new Error(`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.statusText}`) + } + const data = await response.json() + try { + const pkg_info_uri = data.result[0].links.pkg_info_uri + return getLatestMandrelReleaseUrlHelper(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(java_version: string, pkg_info_uri: string): Promise { + const response = await fetch(pkg_info_uri) + if (!response.ok) { + throw new Error(`Failed to fetch package info of latest Mandrel release for Java ${java_version} from DISCO API: ${response.statusText}`) + } + const data = await response.json() + try { + return data.result[0].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}`) } - throw new Error(`Could not find latest Mandrel release: ${tag_name}`) } async function setUpMandrelRelease( From cde0daed27f8a52670dd9f7ed5def5021b3357d0 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Oct 2023 17:24:23 +0200 Subject: [PATCH 3/9] Relax requirement of mandrel versions starting with mandrel- Since we can now define the distribution there is no longer the need to include the mandrel- prefix in Mandrel versions. --- __tests__/mandrel.test.ts | 12 ++- dist/cleanup/index.js | 55 ++++--------- dist/main/index.js | 164 +++++++++++++++++++++++--------------- src/main.ts | 8 +- src/mandrel.ts | 89 ++++++++++++++------- 5 files changed, 184 insertions(+), 144 deletions(-) diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts index 8cb4b8e..cc9f5da 100644 --- a/__tests__/mandrel.test.ts +++ b/__tests__/mandrel.test.ts @@ -9,7 +9,7 @@ process.env['RUNNER_TEMP'] = path.join(__dirname, 'TEMP') test('request invalid version/javaVersion combination', async () => { for (var combination of [ ['mandrel-23.1.1.0-Final', '17'], - ['mandrel-23.0.2.1-Final', '21'], + ['mandrel-23.0.2.1-Final', '21'] ]) { let error = new Error('unexpected') try { @@ -29,7 +29,7 @@ test('request invalid version/javaVersion combination', async () => { test('request invalid version', async () => { for (var combination of [ ['mandrel-23.1.1.0', '21'], - ['mandrel-23.0.2.1', '17'], + ['mandrel-23.0.2.1', '17'] ]) { let error = new Error('unexpected') try { @@ -54,15 +54,19 @@ test('find latest', async () => { expect(tag_name).toContain(mandrel.MANDREL_TAG_PREFIX) }) -test('get latest Mandrel for specific JDK', async () => { +test('get known latest Mandrel for specific JDK', async () => { // Test deprecated versions that won't get updates anymore for (var combination of [ ['11', '22.2.0.0-Final'], - ['20', '23.0.1.2-Final']]) { + ['20', '23.0.1.2-Final'] + ]) { const latest = await mandrel.getLatestMandrelReleaseUrl(combination[0]) expect(latest).toContain(`mandrel-java${combination[0]}`) expect(latest).toContain(combination[1]) } +}) + +test('get latest Mandrel for specific JDK', async () => { // Test supported versions for (var javaVersion of ['17', '21']) { const latest = await mandrel.getLatestMandrelReleaseUrl(javaVersion) diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index c6200c0..2bed224 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -58304,7 +58304,7 @@ class Range { this.set = this.raw .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r.trim())) + .map(r => this.parseRange(r)) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. @@ -58364,18 +58364,15 @@ class Range { const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) debug('comparator trim', range) // `~ 1.2.3` => `~1.2.3` range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - debug('tilde trim', range) // `^ 1.2.3` => `^1.2.3` range = range.replace(re[t.CARETTRIM], caretTrimReplace) - debug('caret trim', range) // At this point, the range is completely trimmed and // ready to be split into comparators. @@ -59677,10 +59674,6 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 -// Max safe length for a build identifier. The max length minus 6 characters for -// the shortest version with a build 0.0.0+BUILD. -const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 - const RELEASE_TYPES = [ 'major', 'premajor', @@ -59694,7 +59687,6 @@ const RELEASE_TYPES = [ module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, @@ -59776,11 +59768,7 @@ module.exports = parseOptions /***/ 9523: /***/ ((module, exports, __nccwpck_require__) => { -const { - MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, - MAX_LENGTH, -} = __nccwpck_require__(2293) +const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293) const debug = __nccwpck_require__(427) exports = module.exports = {} @@ -59791,31 +59779,16 @@ const src = exports.src = [] const t = exports.t = {} let R = 0 -const LETTERDASHNUMBER = '[a-zA-Z0-9-]' - -// Replace some greedy regex tokens to prevent regex dos issues. These regex are -// used internally via the safeRe object since all inputs in this library get -// normalized first to trim and collapse all extra whitespace. The original -// regexes are exported for userland consumption and lower level usage. A -// future breaking change could export the safer regex only with a note that -// all input should have extra whitespace removed. -const safeRegexReplacements = [ - ['\\s', 1], - ['\\d', MAX_LENGTH], - [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], -] - -const makeSafeRegex = (value) => { - for (const [token, max] of safeRegexReplacements) { - value = value - .split(`${token}*`).join(`${token}{0,${max}}`) - .split(`${token}+`).join(`${token}{1,${max}}`) - } - return value -} - const createToken = (name, value, isGlobal) => { - const safe = makeSafeRegex(value) + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + const safe = value + .split('\\s*').join('\\s{0,1}') + .split('\\s+').join('\\s') const index = R++ debug(name, index, value) t[name] = index @@ -59831,13 +59804,13 @@ const createToken = (name, value, isGlobal) => { // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') -createToken('NUMERICIDENTIFIERLOOSE', '\\d+') +createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) +createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') // ## Main Version // Three dot-separated numeric identifiers. @@ -59872,7 +59845,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) +createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata diff --git a/dist/main/index.js b/dist/main/index.js index 0697f8f..15cd593 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -58304,7 +58304,7 @@ class Range { this.set = this.raw .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r.trim())) + .map(r => this.parseRange(r)) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. @@ -58364,18 +58364,15 @@ class Range { const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) debug('comparator trim', range) // `~ 1.2.3` => `~1.2.3` range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - debug('tilde trim', range) // `^ 1.2.3` => `^1.2.3` range = range.replace(re[t.CARETTRIM], caretTrimReplace) - debug('caret trim', range) // At this point, the range is completely trimmed and // ready to be split into comparators. @@ -59677,10 +59674,6 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 -// Max safe length for a build identifier. The max length minus 6 characters for -// the shortest version with a build 0.0.0+BUILD. -const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 - const RELEASE_TYPES = [ 'major', 'premajor', @@ -59694,7 +59687,6 @@ const RELEASE_TYPES = [ module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, @@ -59776,11 +59768,7 @@ module.exports = parseOptions /***/ 9523: /***/ ((module, exports, __nccwpck_require__) => { -const { - MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, - MAX_LENGTH, -} = __nccwpck_require__(2293) +const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293) const debug = __nccwpck_require__(427) exports = module.exports = {} @@ -59791,31 +59779,16 @@ const src = exports.src = [] const t = exports.t = {} let R = 0 -const LETTERDASHNUMBER = '[a-zA-Z0-9-]' - -// Replace some greedy regex tokens to prevent regex dos issues. These regex are -// used internally via the safeRe object since all inputs in this library get -// normalized first to trim and collapse all extra whitespace. The original -// regexes are exported for userland consumption and lower level usage. A -// future breaking change could export the safer regex only with a note that -// all input should have extra whitespace removed. -const safeRegexReplacements = [ - ['\\s', 1], - ['\\d', MAX_LENGTH], - [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], -] - -const makeSafeRegex = (value) => { - for (const [token, max] of safeRegexReplacements) { - value = value - .split(`${token}*`).join(`${token}{0,${max}}`) - .split(`${token}+`).join(`${token}{1,${max}}`) - } - return value -} - const createToken = (name, value, isGlobal) => { - const safe = makeSafeRegex(value) + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + const safe = value + .split('\\s*').join('\\s{0,1}') + .split('\\s+').join('\\s') const index = R++ debug(name, index, value) t[name] = index @@ -59831,13 +59804,13 @@ const createToken = (name, value, isGlobal) => { // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') -createToken('NUMERICIDENTIFIERLOOSE', '\\d+') +createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) +createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') // ## Main Version // Three dot-separated numeric identifiers. @@ -59872,7 +59845,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) +createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata @@ -71418,12 +71391,7 @@ function run() { graalVMHome = yield graalvm.setUpGraalVMJDKCE(javaVersion); break; case c.DISTRIBUTION_MANDREL: - if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) { - graalVMHome = yield (0, mandrel_1.setUpMandrel)(graalVMVersion, javaVersion); - } - else { - throw new Error(`Mandrel requires the 'version' option (see https://github.com/graalvm/setup-graalvm/tree/main#options).`); - } + graalVMHome = yield (0, mandrel_1.setUpMandrel)(graalVMVersion, javaVersion); break; case '': if (javaVersion === c.VERSION_DEV) { @@ -71541,23 +71509,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.setUpMandrel = void 0; +exports.stripMandrelNamespace = exports.getLatestMandrelReleaseUrl = exports.setUpMandrel = exports.MANDREL_TAG_PREFIX = exports.MANDREL_REPO = void 0; const c = __importStar(__nccwpck_require__(9042)); +const httpClient = __importStar(__nccwpck_require__(9925)); const utils_1 = __nccwpck_require__(1314); const tool_cache_1 = __nccwpck_require__(7784); -const MANDREL_REPO = 'mandrel'; -const MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE; +const path_1 = __nccwpck_require__(1017); +exports.MANDREL_REPO = 'mandrel'; +exports.MANDREL_TAG_PREFIX = c.MANDREL_NAMESPACE; const MANDREL_DL_BASE = 'https://github.com/graalvm/mandrel/releases/download'; -function setUpMandrel(graalvmVersion, javaVersion) { +const DISCO_API_BASE = 'https://api.foojay.io/disco/v3.0/packages/jdks'; +function setUpMandrel(mandrelVersion, javaVersion) { return __awaiter(this, void 0, void 0, function* () { - const mandrelVersion = graalvmVersion.substring(c.MANDREL_NAMESPACE.length, graalvmVersion.length); + const version = stripMandrelNamespace(mandrelVersion); let mandrelHome; - switch (mandrelVersion) { + switch (version) { case 'latest': mandrelHome = yield setUpMandrelLatest(javaVersion); break; default: - mandrelHome = yield setUpMandrelRelease(mandrelVersion, javaVersion); + mandrelHome = yield setUpMandrelRelease(version, javaVersion); break; } return mandrelHome; @@ -71566,21 +71537,79 @@ function setUpMandrel(graalvmVersion, javaVersion) { exports.setUpMandrel = setUpMandrel; function setUpMandrelLatest(javaVersion) { return __awaiter(this, void 0, void 0, function* () { - const latestRelease = yield (0, utils_1.getLatestRelease)(MANDREL_REPO); - const tag_name = latestRelease.tag_name; - if (tag_name.startsWith(MANDREL_TAG_PREFIX)) { - const latestVersion = tag_name.substring(MANDREL_TAG_PREFIX.length, tag_name.length); - return setUpMandrelRelease(latestVersion, javaVersion); + const latest_release_url = yield getLatestMandrelReleaseUrl(javaVersion); + const version_tag = getTagFromURI(latest_release_url); + const version = stripMandrelNamespace(version_tag); + const toolName = determineToolName(javaVersion); + return (0, utils_1.downloadExtractAndCacheJDK)(() => __awaiter(this, void 0, void 0, function* () { return (0, tool_cache_1.downloadTool)(latest_release_url); }), toolName, version); + }); +} +// Download URIs are of the form https://github.com/graalvm/mandrel/releases/download// +function getTagFromURI(uri) { + const parts = uri.split('/'); + try { + return parts[parts.length - 2]; + } + catch (error) { + throw new Error(`Failed to extract tag from URI ${uri}: ${error}`); + } +} +function getLatestMandrelReleaseUrl(javaVersion) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + 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('http-client-tests'); + const response = yield _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 = (_a = response.result) === null || _a === void 0 ? void 0 : _a.result[0]; + try { + const pkg_info_uri = result.links.pkg_info_uri; + return yield getLatestMandrelReleaseUrlHelper(_http, javaVersion, pkg_info_uri); + } + catch (error) { + throw new Error(`Failed to get latest Mandrel release for Java ${javaVersion} from DISCO API: ${error}`); + } + }); +} +exports.getLatestMandrelReleaseUrl = getLatestMandrelReleaseUrl; +function getLatestMandrelReleaseUrlHelper(_http, java_version, pkg_info_uri) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const response = yield _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 = (_a = response.result) === null || _a === void 0 ? void 0 : _a.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}`); } - throw new Error(`Could not find latest Mandrel release: ${tag_name}`); }); } function setUpMandrelRelease(version, javaVersion) { return __awaiter(this, void 0, void 0, function* () { - const identifier = determineMandrelIdentifier(version, javaVersion); - const downloadUrl = `${MANDREL_DL_BASE}/${MANDREL_TAG_PREFIX}${version}/${identifier}${c.GRAALVM_FILE_EXTENSION}`; const toolName = determineToolName(javaVersion); - return (0, utils_1.downloadExtractAndCacheJDK)(() => __awaiter(this, void 0, void 0, function* () { return (0, tool_cache_1.downloadTool)(downloadUrl); }), toolName, version); + return (0, utils_1.downloadExtractAndCacheJDK)(() => __awaiter(this, void 0, void 0, function* () { return downloadMandrelJDK(version, javaVersion); }), toolName, version); + }); +} +function downloadMandrelJDK(version, javaVersion) { + return __awaiter(this, void 0, void 0, function* () { + const identifier = determineMandrelIdentifier(version, javaVersion); + const downloadUrl = `${MANDREL_DL_BASE}/${exports.MANDREL_TAG_PREFIX}${version}/${identifier}${c.GRAALVM_FILE_EXTENSION}`; + try { + return yield (0, tool_cache_1.downloadTool)(downloadUrl); + } + catch (error) { + if (error instanceof Error && error.message.includes('404')) { + // Not Found + throw new Error(`Failed to download ${(0, path_1.basename)(downloadUrl)}. Are you sure version: '${version}' and java-version: '${javaVersion}' are correct?`); + } + throw new Error(`Failed to download ${(0, path_1.basename)(downloadUrl)} (error: ${error}).`); + } }); } function determineMandrelIdentifier(version, javaVersion) { @@ -71589,6 +71618,15 @@ function determineMandrelIdentifier(version, javaVersion) { function determineToolName(javaVersion) { return `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}`; } +function stripMandrelNamespace(graalVMVersion) { + if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) { + return graalVMVersion.substring(c.MANDREL_NAMESPACE.length, graalVMVersion.length); + } + else { + return graalVMVersion; + } +} +exports.stripMandrelNamespace = stripMandrelNamespace; /***/ }), diff --git a/src/main.ts b/src/main.ts index f6cd7b5..b89e468 100644 --- a/src/main.ts +++ b/src/main.ts @@ -58,13 +58,7 @@ async function run(): Promise { graalVMHome = await graalvm.setUpGraalVMJDKCE(javaVersion) break case c.DISTRIBUTION_MANDREL: - if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) { - graalVMHome = await setUpMandrel(graalVMVersion, javaVersion) - } else { - throw new Error( - `Mandrel requires the 'version' option (see https://github.com/graalvm/setup-graalvm/tree/main#options).` - ) - } + graalVMHome = await setUpMandrel(graalVMVersion, javaVersion) break case '': if (javaVersion === c.VERSION_DEV) { diff --git a/src/mandrel.ts b/src/mandrel.ts index 35b7f3d..659a9e4 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -1,4 +1,5 @@ import * as c from './constants' +import * as httpClient from '@actions/http-client' import {downloadExtractAndCacheJDK, getLatestRelease} from './utils' import {downloadTool} from '@actions/tool-cache' import {basename} from 'path' @@ -8,22 +9,23 @@ 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 + result: any +} + export async function setUpMandrel( - graalvmVersion: string, + mandrelVersion: string, javaVersion: string ): Promise { - const mandrelVersion = graalvmVersion.substring( - c.MANDREL_NAMESPACE.length, - graalvmVersion.length - ) - + const version = stripMandrelNamespace(mandrelVersion) let mandrelHome - switch (mandrelVersion) { + switch (version) { case 'latest': mandrelHome = await setUpMandrelLatest(javaVersion) break default: - mandrelHome = await setUpMandrelRelease(mandrelVersion, javaVersion) + mandrelHome = await setUpMandrelRelease(version, javaVersion) break } @@ -32,10 +34,9 @@ export async function setUpMandrel( async function setUpMandrelLatest(javaVersion: string): Promise { const latest_release_url = await getLatestMandrelReleaseUrl(javaVersion) - const version_tag = getTagFromURI(latest_release_url); - const version = version_tag.substring(c.MANDREL_NAMESPACE.length, version_tag.length) - console.log(version); - + const version_tag = getTagFromURI(latest_release_url) + const version = stripMandrelNamespace(version_tag) + const toolName = determineToolName(javaVersion) return downloadExtractAndCacheJDK( async () => downloadTool(latest_release_url), @@ -46,39 +47,58 @@ async function setUpMandrelLatest(javaVersion: string): Promise { // Download URIs are of the form https://github.com/graalvm/mandrel/releases/download// function getTagFromURI(uri: string): string { - const parts = uri.split('/'); + const parts = uri.split('/') try { - return parts[parts.length - 2]; + return parts[parts.length - 2] } catch (error) { throw new Error(`Failed to extract tag from URI ${uri}: ${error}`) } } -export async function getLatestMandrelReleaseUrl(javaVersion: string): Promise { +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 response = await fetch(url) - if (!response.ok) { - throw new Error(`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.statusText}`) + const _http = new httpClient.HttpClient('http-client-tests') + 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 data = await response.json() + const result = response.result?.result[0] try { - const pkg_info_uri = data.result[0].links.pkg_info_uri - return getLatestMandrelReleaseUrlHelper(javaVersion, pkg_info_uri) + 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}`) + throw new Error( + `Failed to get latest Mandrel release for Java ${javaVersion} from DISCO API: ${error}` + ) } } -async function getLatestMandrelReleaseUrlHelper(java_version: string, pkg_info_uri: string): Promise { - const response = await fetch(pkg_info_uri) - if (!response.ok) { - throw new Error(`Failed to fetch package info of latest Mandrel release for Java ${java_version} from DISCO API: ${response.statusText}`) +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}` + ) } - const data = await response.json() + const result = response.result?.result[0] try { - return data.result[0].direct_download_uri + 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}`) + throw new Error( + `Failed to get download URI of latest Mandrel release for Java ${java_version} from DISCO API: ${error}` + ) } } @@ -127,3 +147,14 @@ function determineMandrelIdentifier( function determineToolName(javaVersion: string): string { return `mandrel-java${javaVersion}-${c.GRAALVM_PLATFORM}` } + +export function stripMandrelNamespace(graalVMVersion: string) { + if (graalVMVersion.startsWith(c.MANDREL_NAMESPACE)) { + return graalVMVersion.substring( + c.MANDREL_NAMESPACE.length, + graalVMVersion.length + ) + } else { + return graalVMVersion + } +} From 40dc6ae006f84475094a996e325860a10a656fd1 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Mon, 30 Oct 2023 17:29:57 +0200 Subject: [PATCH 4/9] Restore windows latest test & add test for non-prefixed mandrel ver --- .github/workflows/test.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd6f446..6334658 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -187,7 +187,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - version: ['mandrel-22.2.0.0-Final', 'mandrel-latest'] + version: ['mandrel-22.2.0.0-Final', '23.0.1.2-Final', 'mandrel-latest'] java-version: ['17'] distribution: ['mandrel'] os: [windows-latest, ubuntu-latest] @@ -196,10 +196,6 @@ jobs: java-version: '17' distribution: '' # test empty distribution for backward compatibility os: ubuntu-latest - exclude: # temporarily disable Mandrel latest builds on Windows due to unavailability - - version: 'mandrel-latest' - java-version: '17' - os: windows-latest steps: - uses: actions/checkout@v4 - name: Run setup-graalvm action From d5b07dd11879e48ada41b5e14ff12b365c6692de Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 3 Nov 2023 12:36:12 +0100 Subject: [PATCH 5/9] Lock `java-version` to `17.0.8`. This is the last release with access to all tested GU components. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6334658..469eabb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -299,7 +299,7 @@ jobs: - name: Run setup-graalvm action uses: ./ with: - java-version: '17' + java-version: '17.0.8' distribution: 'graalvm' components: 'espresso,llvm-toolchain,native-image,nodejs,python,ruby,wasm' set-java-home: 'false' From 0b782b6b90f760af614396b5c119aeaef4377870 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 3 Nov 2023 13:01:11 +0100 Subject: [PATCH 6/9] Support fixed GA versions of Oracle GraalVM. --- .github/workflows/test.yml | 3 ++ __tests__/mandrel.test.ts | 1 - dist/cleanup/index.js | 55 ++++++++++++++++++------ dist/main/index.js | 88 +++++++++++++++++++++++++++----------- src/graalvm.ts | 26 ++++++++--- 5 files changed, 128 insertions(+), 45 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 469eabb..04b5bf3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,6 +40,9 @@ jobs: distribution: 'graalvm-community' os: ubuntu-latest components: 'native-image' # should print a warning but not fail + - java-version: '21.0.0' # test for GA version (see #63) + distribution: 'graalvm' + os: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run setup-graalvm action diff --git a/__tests__/mandrel.test.ts b/__tests__/mandrel.test.ts index cc9f5da..efa78ce 100644 --- a/__tests__/mandrel.test.ts +++ b/__tests__/mandrel.test.ts @@ -73,4 +73,3 @@ test('get latest Mandrel for specific JDK', async () => { expect(latest).toContain(`mandrel-java${javaVersion}`) } }) - diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 2bed224..c6200c0 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -58304,7 +58304,7 @@ class Range { this.set = this.raw .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r)) + .map(r => this.parseRange(r.trim())) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. @@ -58364,15 +58364,18 @@ class Range { const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) debug('comparator trim', range) // `~ 1.2.3` => `~1.2.3` range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + debug('tilde trim', range) // `^ 1.2.3` => `^1.2.3` range = range.replace(re[t.CARETTRIM], caretTrimReplace) + debug('caret trim', range) // At this point, the range is completely trimmed and // ready to be split into comparators. @@ -59674,6 +59677,10 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 +// Max safe length for a build identifier. The max length minus 6 characters for +// the shortest version with a build 0.0.0+BUILD. +const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + const RELEASE_TYPES = [ 'major', 'premajor', @@ -59687,6 +59694,7 @@ const RELEASE_TYPES = [ module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, @@ -59768,7 +59776,11 @@ module.exports = parseOptions /***/ 9523: /***/ ((module, exports, __nccwpck_require__) => { -const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293) +const { + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_LENGTH, +} = __nccwpck_require__(2293) const debug = __nccwpck_require__(427) exports = module.exports = {} @@ -59779,16 +59791,31 @@ const src = exports.src = [] const t = exports.t = {} let R = 0 +const LETTERDASHNUMBER = '[a-zA-Z0-9-]' + +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +const safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] + +const makeSafeRegex = (value) => { + for (const [token, max] of safeRegexReplacements) { + value = value + .split(`${token}*`).join(`${token}{0,${max}}`) + .split(`${token}+`).join(`${token}{1,${max}}`) + } + return value +} + const createToken = (name, value, isGlobal) => { - // Replace all greedy whitespace to prevent regex dos issues. These regex are - // used internally via the safeRe object since all inputs in this library get - // normalized first to trim and collapse all extra whitespace. The original - // regexes are exported for userland consumption and lower level usage. A - // future breaking change could export the safer regex only with a note that - // all input should have extra whitespace removed. - const safe = value - .split('\\s*').join('\\s{0,1}') - .split('\\s+').join('\\s') + const safe = makeSafeRegex(value) const index = R++ debug(name, index, value) t[name] = index @@ -59804,13 +59831,13 @@ const createToken = (name, value, isGlobal) => { // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') -createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') +createToken('NUMERICIDENTIFIERLOOSE', '\\d+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') +createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) // ## Main Version // Three dot-separated numeric identifiers. @@ -59845,7 +59872,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') +createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata diff --git a/dist/main/index.js b/dist/main/index.js index 15cd593..2b089f2 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -8029,7 +8029,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; const http = __importStar(__nccwpck_require__(3685)); const https = __importStar(__nccwpck_require__(5687)); -const pm = __importStar(__nccwpck_require__(3466)); +const pm = __importStar(__nccwpck_require__(3186)); const tunnel = __importStar(__nccwpck_require__(4294)); var HttpCodes; (function (HttpCodes) { @@ -8603,7 +8603,7 @@ const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCa /***/ }), -/***/ 3466: +/***/ 3186: /***/ ((__unused_webpack_module, exports) => { "use strict"; @@ -58304,7 +58304,7 @@ class Range { this.set = this.raw .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r)) + .map(r => this.parseRange(r.trim())) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. @@ -58364,15 +58364,18 @@ class Range { const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] range = range.replace(hr, hyphenReplace(this.options.includePrerelease)) debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) debug('comparator trim', range) // `~ 1.2.3` => `~1.2.3` range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + debug('tilde trim', range) // `^ 1.2.3` => `^1.2.3` range = range.replace(re[t.CARETTRIM], caretTrimReplace) + debug('caret trim', range) // At this point, the range is completely trimmed and // ready to be split into comparators. @@ -59185,7 +59188,7 @@ module.exports = cmp /***/ }), -/***/ 5280: +/***/ 3466: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const SemVer = __nccwpck_require__(8088) @@ -59594,7 +59597,7 @@ const neq = __nccwpck_require__(6017) const gte = __nccwpck_require__(5522) const lte = __nccwpck_require__(7520) const cmp = __nccwpck_require__(5098) -const coerce = __nccwpck_require__(5280) +const coerce = __nccwpck_require__(3466) const Comparator = __nccwpck_require__(1532) const Range = __nccwpck_require__(9828) const satisfies = __nccwpck_require__(6055) @@ -59674,6 +59677,10 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 +// Max safe length for a build identifier. The max length minus 6 characters for +// the shortest version with a build 0.0.0+BUILD. +const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 + const RELEASE_TYPES = [ 'major', 'premajor', @@ -59687,6 +59694,7 @@ const RELEASE_TYPES = [ module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, @@ -59768,7 +59776,11 @@ module.exports = parseOptions /***/ 9523: /***/ ((module, exports, __nccwpck_require__) => { -const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293) +const { + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_LENGTH, +} = __nccwpck_require__(2293) const debug = __nccwpck_require__(427) exports = module.exports = {} @@ -59779,16 +59791,31 @@ const src = exports.src = [] const t = exports.t = {} let R = 0 +const LETTERDASHNUMBER = '[a-zA-Z0-9-]' + +// Replace some greedy regex tokens to prevent regex dos issues. These regex are +// used internally via the safeRe object since all inputs in this library get +// normalized first to trim and collapse all extra whitespace. The original +// regexes are exported for userland consumption and lower level usage. A +// future breaking change could export the safer regex only with a note that +// all input should have extra whitespace removed. +const safeRegexReplacements = [ + ['\\s', 1], + ['\\d', MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], +] + +const makeSafeRegex = (value) => { + for (const [token, max] of safeRegexReplacements) { + value = value + .split(`${token}*`).join(`${token}{0,${max}}`) + .split(`${token}+`).join(`${token}{1,${max}}`) + } + return value +} + const createToken = (name, value, isGlobal) => { - // Replace all greedy whitespace to prevent regex dos issues. These regex are - // used internally via the safeRe object since all inputs in this library get - // normalized first to trim and collapse all extra whitespace. The original - // regexes are exported for userland consumption and lower level usage. A - // future breaking change could export the safer regex only with a note that - // all input should have extra whitespace removed. - const safe = value - .split('\\s*').join('\\s{0,1}') - .split('\\s+').join('\\s') + const safe = makeSafeRegex(value) const index = R++ debug(name, index, value) t[name] = index @@ -59804,13 +59831,13 @@ const createToken = (name, value, isGlobal) => { // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') -createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') +createToken('NUMERICIDENTIFIERLOOSE', '\\d+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') +createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) // ## Main Version // Three dot-separated numeric identifiers. @@ -59845,7 +59872,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') +createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata @@ -71001,11 +71028,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setUpGraalVMRelease = exports.findGraalVMVersion = exports.setUpGraalVMLatest_22_X = exports.findHighestJavaVersion = exports.setUpGraalVMJDKDevBuild = exports.findLatestGraalVMJDKCEJavaVersion = exports.setUpGraalVMJDKCE = exports.setUpGraalVMJDK = void 0; const c = __importStar(__nccwpck_require__(9042)); +const semver = __importStar(__nccwpck_require__(1383)); const utils_1 = __nccwpck_require__(1314); const gds_1 = __nccwpck_require__(9543); const tool_cache_1 = __nccwpck_require__(7784); const path_1 = __nccwpck_require__(1017); -const semver_1 = __nccwpck_require__(1383); const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_CE_DL_BASE = `https://github.com/graalvm/${c.GRAALVM_RELEASES_REPO}/releases/download`; const GRAALVM_REPO_DEV_BUILDS = 'graalvm-ce-dev-builds'; @@ -71018,11 +71045,24 @@ function setUpGraalVMJDK(javaVersionOrDev) { return setUpGraalVMJDKDevBuild(); } const javaVersion = javaVersionOrDev; - const toolName = determineToolName(javaVersion, false); + let toolName = determineToolName(javaVersion, false); let downloadUrl; if (javaVersion.includes('.')) { - const majorJavaVersion = javaVersion.split('.')[0]; - downloadUrl = `${GRAALVM_DL_BASE}/${majorJavaVersion}/archive/${toolName}${c.GRAALVM_FILE_EXTENSION}`; + if (semver.valid(javaVersion)) { + const majorJavaVersion = semver.major(javaVersion); + const minorJavaVersion = semver.minor(javaVersion); + const patchJavaVersion = semver.patch(javaVersion); + const isGARelease = minorJavaVersion === 0 && patchJavaVersion === 0; + let downloadName = toolName; + if (isGARelease) { + // For GA versions of JDKs, /archive/ does not use minor and patch version (see https://www.oracle.com/java/technologies/jdk-script-friendly-urls/) + downloadName = determineToolName(majorJavaVersion.toString(), false); + } + downloadUrl = `${GRAALVM_DL_BASE}/${majorJavaVersion}/archive/${downloadName}${c.GRAALVM_FILE_EXTENSION}`; + } + else { + throw new Error(`java-version set to '${javaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}`); + } } else { downloadUrl = `${GRAALVM_DL_BASE}/${javaVersion}/latest/${toolName}${c.GRAALVM_FILE_EXTENSION}`; @@ -71059,8 +71099,8 @@ function findLatestGraalVMJDKCEJavaVersion(majorJavaVersion) { const versionNumberStartIndex = `refs/tags/${GRAALVM_JDK_TAG_PREFIX}`.length; for (const matchingRef of matchingRefs) { const currentVersion = matchingRef.ref.substring(versionNumberStartIndex); - if ((0, semver_1.valid)(currentVersion) && - (0, semver_1.gt)(currentVersion, highestVersion)) { + if (semver.valid(currentVersion) && + semver.gt(currentVersion, highestVersion)) { highestVersion = currentVersion; } } diff --git a/src/graalvm.ts b/src/graalvm.ts index edf7ef7..f2be101 100644 --- a/src/graalvm.ts +++ b/src/graalvm.ts @@ -1,4 +1,5 @@ import * as c from './constants' +import * as semver from 'semver' import { downloadAndExtractJDK, downloadExtractAndCacheJDK, @@ -9,7 +10,6 @@ import { import {downloadGraalVMEELegacy} from './gds' import {downloadTool} from '@actions/tool-cache' import {basename} from 'path' -import {gt as semverGt, valid as semverValid} from 'semver' const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm' const GRAALVM_CE_DL_BASE = `https://github.com/graalvm/${c.GRAALVM_RELEASES_REPO}/releases/download` @@ -26,11 +26,25 @@ export async function setUpGraalVMJDK( return setUpGraalVMJDKDevBuild() } const javaVersion = javaVersionOrDev - const toolName = determineToolName(javaVersion, false) + let toolName = determineToolName(javaVersion, false) let downloadUrl: string if (javaVersion.includes('.')) { - const majorJavaVersion = javaVersion.split('.')[0] - downloadUrl = `${GRAALVM_DL_BASE}/${majorJavaVersion}/archive/${toolName}${c.GRAALVM_FILE_EXTENSION}` + if (semver.valid(javaVersion)) { + const majorJavaVersion = semver.major(javaVersion) + const minorJavaVersion = semver.minor(javaVersion) + const patchJavaVersion = semver.patch(javaVersion) + const isGARelease = minorJavaVersion === 0 && patchJavaVersion === 0 + let downloadName = toolName + if (isGARelease) { + // For GA versions of JDKs, /archive/ does not use minor and patch version (see https://www.oracle.com/java/technologies/jdk-script-friendly-urls/) + downloadName = determineToolName(majorJavaVersion.toString(), false) + } + downloadUrl = `${GRAALVM_DL_BASE}/${majorJavaVersion}/archive/${downloadName}${c.GRAALVM_FILE_EXTENSION}` + } else { + throw new Error( + `java-version set to '${javaVersion}'. Please make sure the java-version is set correctly. ${c.ERROR_HINT}` + ) + } } else { downloadUrl = `${GRAALVM_DL_BASE}/${javaVersion}/latest/${toolName}${c.GRAALVM_FILE_EXTENSION}` } @@ -71,8 +85,8 @@ export async function findLatestGraalVMJDKCEJavaVersion( for (const matchingRef of matchingRefs) { const currentVersion = matchingRef.ref.substring(versionNumberStartIndex) if ( - semverValid(currentVersion) && - semverGt(currentVersion, highestVersion) + semver.valid(currentVersion) && + semver.gt(currentVersion, highestVersion) ) { highestVersion = currentVersion } From 2a93b69fdf86ac5a078a98c1a707744632e1da94 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Fri, 3 Nov 2023 13:12:28 +0100 Subject: [PATCH 7/9] Bump version to `1.1.5`. --- dist/main/index.js | 2 +- package-lock.json | 4 ++-- package.json | 2 +- src/gds.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/main/index.js b/dist/main/index.js index 2b089f2..b97608f 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -70844,7 +70844,7 @@ const assert_1 = __nccwpck_require__(9491); const uuid_1 = __nccwpck_require__(5840); function downloadGraalVMEELegacy(gdsToken, version, javaVersion) { return __awaiter(this, void 0, void 0, function* () { - const userAgent = `GraalVMGitHubAction/1.1.4 (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})`; + const userAgent = `GraalVMGitHubAction/1.1.5 (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})`; const baseArtifact = yield fetchArtifact(userAgent, 'isBase:True', version, javaVersion); return downloadArtifact(gdsToken, userAgent, baseArtifact); }); diff --git a/package-lock.json b/package-lock.json index 826814c..0dbd4b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "setup-graalvm", - "version": "1.1.4", + "version": "1.1.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "setup-graalvm", - "version": "1.1.4", + "version": "1.1.5", "license": "UPL", "dependencies": { "@actions/cache": "^3.0.4", diff --git a/package.json b/package.json index e899bad..573686b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "setup-graalvm", - "version": "1.1.4", + "version": "1.1.5", "private": true, "description": "GitHub Action for GraalVM", "main": "lib/main.js", diff --git a/src/gds.ts b/src/gds.ts index dc1bbb5..695b31f 100644 --- a/src/gds.ts +++ b/src/gds.ts @@ -32,7 +32,7 @@ export async function downloadGraalVMEELegacy( version: string, javaVersion: string ): Promise { - const userAgent = `GraalVMGitHubAction/1.1.4 (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})` + const userAgent = `GraalVMGitHubAction/1.1.5 (arch:${c.GRAALVM_ARCH}; os:${c.GRAALVM_PLATFORM}; java:${javaVersion})` const baseArtifact = await fetchArtifact( userAgent, 'isBase:True', From 1c219f5b277aecf69f4c90dc8c61246441a6cf84 Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Fri, 3 Nov 2023 14:08:39 +0200 Subject: [PATCH 8/9] Remove user-agent from HttpClient --- src/mandrel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mandrel.ts b/src/mandrel.ts index 659a9e4..8e429ac 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -59,7 +59,7 @@ 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('http-client-tests') + const _http = new httpClient.HttpClient() const response = await _http.getJson(url) if (response.statusCode !== 200) { throw new Error( From b8dc5fccfbc65b21dd26e8341e7b21c86547f61b Mon Sep 17 00:00:00 2001 From: Foivos Zakkak Date: Fri, 3 Nov 2023 14:21:41 +0200 Subject: [PATCH 9/9] Get latest mandrel release if version not specified Update doc. --- .github/workflows/test.yml | 4 ++++ README.md | 2 +- dist/main/index.js | 4 +++- src/mandrel.ts | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04b5bf3..a0e669c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -199,6 +199,10 @@ jobs: java-version: '17' distribution: '' # test empty distribution for backward compatibility os: ubuntu-latest + - version: '' # test with no version + java-version: '21' + distribution: 'mandrel' + os: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run setup-graalvm action diff --git a/README.md b/README.md index 24bc3ce..bf349c0 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ jobs: | `native-image-job-reports` *) | `'false'` | If set to `'true'`, post a job summary containing a Native Image build report. | | `native-image-pr-reports` *) | `'false'` | If set to `'true'`, post a comment containing a Native Image build report on pull requests. Requires `write` permissions for the [`pull-requests` scope][gha-permissions]. | | `components` | `''` | Comma-separated list of GraalVM components (e.g., `native-image` or `ruby,nodejs`) that will be installed by the [GraalVM Updater][gu]. | -| `version` | `''` | `X.Y.Z` (e.g., `22.3.0`) for a specific [GraalVM release][releases] up to `22.3.2`
`mandrel-X.Y.Z` (e.g., `mandrel-21.3.0.0-Final`) for a specific [Mandrel release][mandrel-releases],
`mandrel-latest` for [latest Mandrel stable release][mandrel-stable]. | +| `version` | `''` | `X.Y.Z` (e.g., `22.3.0`) for a specific [GraalVM release][releases] up to `22.3.2`
`mandrel-X.Y.Z.W` or `X.Y.Z.W-Final` (e.g., `mandrel-21.3.0.0-Final` or `21.3.0.0-Final`) for a specific [Mandrel release][mandrel-releases],
`mandrel-latest` or `latest` for the latest Mandrel stable release. | | `gds-token` | `''` | Download token for the GraalVM Download Service. If a non-empty token is provided, the action will set up GraalVM Enterprise Edition (see [GraalVM EE template](#template-for-graalvm-enterprise-edition)). | **) Make sure that Native Image is used only once per build job. Otherwise, the report is only generated for the last Native Image build.* diff --git a/dist/main/index.js b/dist/main/index.js index b97608f..fb914e0 100644 --- a/dist/main/index.js +++ b/dist/main/index.js @@ -71564,6 +71564,8 @@ function setUpMandrel(mandrelVersion, javaVersion) { const version = stripMandrelNamespace(mandrelVersion); let mandrelHome; switch (version) { + case '': + // fetch latest if no version is specified case 'latest': mandrelHome = yield setUpMandrelLatest(javaVersion); break; @@ -71598,7 +71600,7 @@ function getLatestMandrelReleaseUrl(javaVersion) { var _a; return __awaiter(this, void 0, void 0, function* () { 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('http-client-tests'); + const _http = new httpClient.HttpClient(); const response = yield _http.getJson(url); if (response.statusCode !== 200) { throw new Error(`Failed to fetch latest Mandrel release for Java ${javaVersion} from DISCO API: ${response.result}`); diff --git a/src/mandrel.ts b/src/mandrel.ts index 8e429ac..ae79413 100644 --- a/src/mandrel.ts +++ b/src/mandrel.ts @@ -21,6 +21,8 @@ export async function setUpMandrel( const version = stripMandrelNamespace(mandrelVersion) let mandrelHome switch (version) { + case '': + // fetch latest if no version is specified case 'latest': mandrelHome = await setUpMandrelLatest(javaVersion) break