From f0a7c591b01947e0d87ac10fcad25639d86deb36 Mon Sep 17 00:00:00 2001 From: Fabio Niephaus Date: Thu, 5 Sep 2024 10:12:35 +0200 Subject: [PATCH] Add support for EA builds of Oracle GraalVM --- .github/workflows/e2e-versions.yml | 3 + .../distributors/graalvm-installer.test.ts | 53 +++++++++++-- dist/setup/index.js | 53 +++++++++++-- src/distributions/graalvm/installer.ts | 74 +++++++++++++++++-- src/distributions/graalvm/models.ts | 13 ++++ 5 files changed, 180 insertions(+), 16 deletions(-) diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 23a6e69..045dee1 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -58,6 +58,9 @@ jobs: - distribution: graalvm os: ubuntu-latest version: 21 + - distribution: graalvm + os: ubuntu-latest + version: '24-ea' steps: - name: Checkout uses: actions/checkout@v4 diff --git a/__tests__/distributors/graalvm-installer.test.ts b/__tests__/distributors/graalvm-installer.test.ts index 90944c6..ae2db43 100644 --- a/__tests__/distributors/graalvm-installer.test.ts +++ b/__tests__/distributors/graalvm-installer.test.ts @@ -66,6 +66,37 @@ describe('findPackageForDownload', () => { expect(result.url).toBe(url); }); + it.each([ + [ + '24-ea', + /^https:\/\/github\.com\/graalvm\/oracle-graalvm-ea-builds\/releases\/download\/jdk-24\.0\.0-ea\./ + ] + ])('version is %s -> %s', async (version, expectedUrlPrefix) => { + /* Needed only for this particular test because /latest/ urls tend to change */ + spyHttpClient = jest.spyOn(HttpClient.prototype, 'head'); + spyHttpClient.mockReturnValue( + Promise.resolve({ + message: { + statusCode: 200 + } + }) + ); + + const eaDistro = new GraalVMDistribution({ + version, + architecture: '', // to get default value + packageType: 'jdk', + checkLatest: false + }); + + const versionWithoutEA = version.split('-')[0]; + const result = await eaDistro['findPackageForDownload'](versionWithoutEA); + + jest.restoreAllMocks(); + + expect(result.url).toEqual(expect.stringMatching(expectedUrlPrefix)); + }); + it.each([ ['amd64', 'x64'], ['arm64', 'aarch64'] @@ -75,7 +106,7 @@ describe('findPackageForDownload', () => { jest.spyOn(os, 'arch').mockReturnValue(osArch); jest.spyOn(os, 'platform').mockReturnValue('linux'); - const version = '17'; + const version = '21'; const distro = new GraalVMDistribution({ version, architecture: '', // to get default value @@ -89,7 +120,7 @@ describe('findPackageForDownload', () => { } const archiveType = getDownloadArchiveExtension(); const result = await distro['findPackageForDownload'](version); - const expectedUrl = `https://download.oracle.com/graalvm/17/latest/graalvm-jdk-17_${osType}-${distroArch}_bin.${archiveType}`; + const expectedUrl = `https://download.oracle.com/graalvm/21/latest/graalvm-jdk-21_${osType}-${distroArch}_bin.${archiveType}`; expect(result.url).toBe(expectedUrl); } @@ -97,13 +128,25 @@ describe('findPackageForDownload', () => { it('should throw an error', async () => { await expect(distribution['findPackageForDownload']('8')).rejects.toThrow( - /GraalVM JDK is only supported for JDK 17 and later/ + /GraalVM is only supported for JDK 17 and later/ ); await expect(distribution['findPackageForDownload']('11')).rejects.toThrow( - /GraalVM JDK is only supported for JDK 17 and later/ + /GraalVM is only supported for JDK 17 and later/ ); await expect(distribution['findPackageForDownload']('18')).rejects.toThrow( - /Could not find GraalVM JDK for SemVer */ + /Could not find GraalVM for SemVer */ + ); + + const unavailableEADistro = new GraalVMDistribution({ + version: '17-ea', + architecture: '', // to get default value + packageType: 'jdk', + checkLatest: false + }); + await expect( + unavailableEADistro['findPackageForDownload']('17') + ).rejects.toThrow( + /No GraalVM EA build found\. Are you sure java-version: '17-ea' is correct\?/ ); }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 9f76be3..28a917e 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -124366,6 +124366,8 @@ const base_installer_1 = __nccwpck_require__(59741); const util_1 = __nccwpck_require__(92629); const http_client_1 = __nccwpck_require__(96255); const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; +const IS_WINDOWS = process.platform === 'win32'; +const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; class GraalVMDistribution extends base_installer_1.JavaBase { constructor(installerOptions) { super('GraalVM', installerOptions); @@ -124391,10 +124393,10 @@ class GraalVMDistribution extends base_installer_1.JavaBase { throw new Error(`Unsupported architecture: ${this.architecture}`); } if (!this.stable) { - throw new Error('Early access versions are not supported'); + return this.findEABuildDownloadUrl(`${range}-ea`); } if (this.packageType !== 'jdk') { - throw new Error('GraalVM JDK provides only the `jdk` package type'); + throw new Error('GraalVM provides only the `jdk` package type'); } const platform = this.getPlatform(); const extension = (0, util_1.getDownloadArchiveExtension)(); @@ -124409,18 +124411,59 @@ class GraalVMDistribution extends base_installer_1.JavaBase { fileUrl = `${GRAALVM_DL_BASE}/${range}/latest/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}`; } if (parseInt(major) < 17) { - throw new Error('GraalVM JDK is only supported for JDK 17 and later'); + throw new Error('GraalVM is only supported for JDK 17 and later'); } const response = yield this.http.head(fileUrl); if (response.message.statusCode === http_client_1.HttpCodes.NotFound) { - throw new Error(`Could not find GraalVM JDK for SemVer ${range}`); + throw new Error(`Could not find GraalVM for SemVer ${range}`); } if (response.message.statusCode !== http_client_1.HttpCodes.OK) { - throw new Error(`Http request for GraalVM JDK failed with status code: ${response.message.statusCode}`); + throw new Error(`Http request for GraalVM failed with status code: ${response.message.statusCode}`); } return { url: fileUrl, version: range }; }); } + findEABuildDownloadUrl(javaEaVersion) { + return __awaiter(this, void 0, void 0, function* () { + const versions = yield this.fetchEAJson(javaEaVersion); + const latestVersion = versions.find(v => v.latest); + if (!latestVersion) { + throw new Error(`Unable to find latest version for '${javaEaVersion}'`); + } + const arch = this.distributionArchitecture(); + const file = latestVersion.files.find(f => f.arch === arch && f.platform === GRAALVM_PLATFORM); + if (!file || !file.filename.startsWith('graalvm-jdk-')) { + throw new Error(`Unable to find file metadata for '${javaEaVersion}'`); + } + return { + url: `${latestVersion.download_base_url}${file.filename}`, + version: latestVersion.version + }; + }); + } + fetchEAJson(javaEaVersion) { + return __awaiter(this, void 0, void 0, function* () { + const owner = 'graalvm'; + const repository = 'oracle-graalvm-ea-builds'; + const branch = 'main'; + const filePath = `versions/${javaEaVersion}.json`; + const url = `https://api.github.com/repos/${owner}/${repository}/contents/${filePath}?ref=${branch}`; + const headers = (0, util_1.getGitHubHttpHeaders)(); + core.debug(`Trying to fetch available version info for GraalVM EA builds from '${url}'`); + let fetchedJson; + try { + fetchedJson = (yield this.http.getJson(url, headers)) + .result; + } + catch (err) { + throw Error(`Fetching version info for GraalVM EA builds from '${url}' failed with the error: ${err.message}`); + } + if (fetchedJson === null) { + throw Error(`No GraalVM EA build found. Are you sure java-version: '${javaEaVersion}' is correct?`); + } + return fetchedJson; + }); + } getPlatform(platform = process.platform) { switch (platform) { case 'darwin': diff --git a/src/distributions/graalvm/installer.ts b/src/distributions/graalvm/installer.ts index d213642..37dbe32 100644 --- a/src/distributions/graalvm/installer.ts +++ b/src/distributions/graalvm/installer.ts @@ -10,10 +10,17 @@ import { JavaInstallerOptions, JavaInstallerResults } from '../base-models'; -import {extractJdkFile, getDownloadArchiveExtension} from '../../util'; +import { + extractJdkFile, + getDownloadArchiveExtension, + getGitHubHttpHeaders +} from '../../util'; import {HttpCodes} from '@actions/http-client'; +import {GraalVMEAVersion} from './models'; const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; +const IS_WINDOWS = process.platform === 'win32'; +const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; export class GraalVMDistribution extends JavaBase { constructor(installerOptions: JavaInstallerOptions) { @@ -56,11 +63,11 @@ export class GraalVMDistribution extends JavaBase { } if (!this.stable) { - throw new Error('Early access versions are not supported'); + return this.findEABuildDownloadUrl(`${range}-ea`); } if (this.packageType !== 'jdk') { - throw new Error('GraalVM JDK provides only the `jdk` package type'); + throw new Error('GraalVM provides only the `jdk` package type'); } const platform = this.getPlatform(); @@ -76,24 +83,79 @@ export class GraalVMDistribution extends JavaBase { } if (parseInt(major) < 17) { - throw new Error('GraalVM JDK is only supported for JDK 17 and later'); + throw new Error('GraalVM is only supported for JDK 17 and later'); } const response = await this.http.head(fileUrl); if (response.message.statusCode === HttpCodes.NotFound) { - throw new Error(`Could not find GraalVM JDK for SemVer ${range}`); + throw new Error(`Could not find GraalVM for SemVer ${range}`); } if (response.message.statusCode !== HttpCodes.OK) { throw new Error( - `Http request for GraalVM JDK failed with status code: ${response.message.statusCode}` + `Http request for GraalVM failed with status code: ${response.message.statusCode}` ); } return {url: fileUrl, version: range}; } + private async findEABuildDownloadUrl( + javaEaVersion: string + ): Promise { + const versions = await this.fetchEAJson(javaEaVersion); + const latestVersion = versions.find(v => v.latest); + if (!latestVersion) { + throw new Error(`Unable to find latest version for '${javaEaVersion}'`); + } + const arch = this.distributionArchitecture(); + const file = latestVersion.files.find( + f => f.arch === arch && f.platform === GRAALVM_PLATFORM + ); + if (!file || !file.filename.startsWith('graalvm-jdk-')) { + throw new Error(`Unable to find file metadata for '${javaEaVersion}'`); + } + return { + url: `${latestVersion.download_base_url}${file.filename}`, + version: latestVersion.version + }; + } + + private async fetchEAJson( + javaEaVersion: string + ): Promise { + const owner = 'graalvm'; + const repository = 'oracle-graalvm-ea-builds'; + const branch = 'main'; + const filePath = `versions/${javaEaVersion}.json`; + + const url = `https://api.github.com/repos/${owner}/${repository}/contents/${filePath}?ref=${branch}`; + + const headers = getGitHubHttpHeaders(); + + core.debug( + `Trying to fetch available version info for GraalVM EA builds from '${url}'` + ); + let fetchedJson; + try { + fetchedJson = (await this.http.getJson(url, headers)) + .result; + } catch (err) { + throw Error( + `Fetching version info for GraalVM EA builds from '${url}' failed with the error: ${ + (err as Error).message + }` + ); + } + if (fetchedJson === null) { + throw Error( + `No GraalVM EA build found. Are you sure java-version: '${javaEaVersion}' is correct?` + ); + } + return fetchedJson; + } + public getPlatform(platform: NodeJS.Platform = process.platform): OsVersions { switch (platform) { case 'darwin': diff --git a/src/distributions/graalvm/models.ts b/src/distributions/graalvm/models.ts index 2838ead..2ae1e4d 100644 --- a/src/distributions/graalvm/models.ts +++ b/src/distributions/graalvm/models.ts @@ -1 +1,14 @@ export type OsVersions = 'linux' | 'macos' | 'windows'; + +export interface GraalVMEAFile { + filename: string; + arch: 'aarch64' | 'x64'; + platform: 'darwin' | 'linux' | 'windows'; +} + +export interface GraalVMEAVersion { + version: string; + latest?: boolean; + download_base_url: string; + files: GraalVMEAFile[]; +}