-
Notifications
You must be signed in to change notification settings - Fork 767
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Eclipse Temurin (#201)
* Add support for Adoptium OpenJDK Refs #191 * Rename distribution to Eclipse Temurin * Update end-to-end tests in GitHub workflows * Exclude e2e tests for Temurin JREs for now * fix version * Update e2e-versions.yml * Handle Eclipse Temurin version suffixes ("beta") * Add test for new version suffix "beta" * Add updated `index.js` * fix an issue Co-authored-by: Maxim Lobanov <[email protected]>
1 parent
ad01d13
commit 4b1b3d4
Showing
10 changed files
with
9,484 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { HttpClient } from '@actions/http-client'; | ||
|
||
import { | ||
TemurinDistribution, | ||
TemurinImplementation | ||
} from '../../src/distributions/temurin/installer'; | ||
import { JavaInstallerOptions } from '../../src/distributions/base-models'; | ||
|
||
let manifestData = require('../data/temurin.json') as []; | ||
|
||
describe('getAvailableVersions', () => { | ||
let spyHttpClient: jest.SpyInstance; | ||
|
||
beforeEach(() => { | ||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson'); | ||
spyHttpClient.mockReturnValue({ | ||
statusCode: 200, | ||
headers: {}, | ||
result: [] | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
jest.clearAllMocks(); | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
it.each([ | ||
[ | ||
{ version: '16', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot, | ||
'os=mac&architecture=x64&image_type=jdk&release_type=ga&jvm_impl=hotspot&page_size=20&page=0' | ||
], | ||
[ | ||
{ version: '16', architecture: 'x86', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot, | ||
'os=mac&architecture=x86&image_type=jdk&release_type=ga&jvm_impl=hotspot&page_size=20&page=0' | ||
], | ||
[ | ||
{ version: '16', architecture: 'x64', packageType: 'jre', checkLatest: false }, | ||
TemurinImplementation.Hotspot, | ||
'os=mac&architecture=x64&image_type=jre&release_type=ga&jvm_impl=hotspot&page_size=20&page=0' | ||
], | ||
[ | ||
{ version: '16-ea', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot, | ||
'os=mac&architecture=x64&image_type=jdk&release_type=ea&jvm_impl=hotspot&page_size=20&page=0' | ||
] | ||
])( | ||
'build correct url for %s', | ||
async ( | ||
installerOptions: JavaInstallerOptions, | ||
impl: TemurinImplementation, | ||
expectedParameters | ||
) => { | ||
const distribution = new TemurinDistribution(installerOptions, impl); | ||
const baseUrl = 'https://api.adoptium.net/v3/assets/version/%5B1.0,100.0%5D'; | ||
const expectedUrl = `${baseUrl}?project=jdk&vendor=adoptium&heap_size=normal&sort_method=DEFAULT&sort_order=DESC&${expectedParameters}`; | ||
distribution['getPlatformOption'] = () => 'mac'; | ||
|
||
await distribution['getAvailableVersions'](); | ||
|
||
expect(spyHttpClient.mock.calls).toHaveLength(1); | ||
expect(spyHttpClient.mock.calls[0][0]).toBe(expectedUrl); | ||
} | ||
); | ||
|
||
it('load available versions', async () => { | ||
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson'); | ||
spyHttpClient | ||
.mockReturnValueOnce({ | ||
statusCode: 200, | ||
headers: {}, | ||
result: manifestData | ||
}) | ||
.mockReturnValueOnce({ | ||
statusCode: 200, | ||
headers: {}, | ||
result: manifestData | ||
}) | ||
.mockReturnValueOnce({ | ||
statusCode: 200, | ||
headers: {}, | ||
result: [] | ||
}); | ||
|
||
const distribution = new TemurinDistribution( | ||
{ version: '8', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot | ||
); | ||
const availableVersions = await distribution['getAvailableVersions'](); | ||
expect(availableVersions).not.toBeNull(); | ||
expect(availableVersions.length).toBe(manifestData.length * 2); | ||
}); | ||
|
||
it.each([ | ||
[TemurinImplementation.Hotspot, 'jdk', 'Java_Temurin-Hotspot_jdk'], | ||
[TemurinImplementation.Hotspot, 'jre', 'Java_Temurin-Hotspot_jre'] | ||
])( | ||
'find right toolchain folder', | ||
(impl: TemurinImplementation, packageType: string, expected: string) => { | ||
const distribution = new TemurinDistribution( | ||
{ version: '8', architecture: 'x64', packageType: packageType, checkLatest: false }, | ||
impl | ||
); | ||
|
||
// @ts-ignore - because it is protected | ||
expect(distribution.toolcacheFolderName).toBe(expected); | ||
} | ||
); | ||
}); | ||
|
||
describe('findPackageForDownload', () => { | ||
it.each([ | ||
['8', '8.0.302+8'], | ||
['16', '16.0.2+7'], | ||
['16.0', '16.0.2+7'], | ||
['16.0.2', '16.0.2+7'], | ||
['8.x', '8.0.302+8'], | ||
['x', '16.0.2+7'] | ||
])('version is resolved correctly %s -> %s', async (input, expected) => { | ||
const distribution = new TemurinDistribution( | ||
{ version: '8', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot | ||
); | ||
distribution['getAvailableVersions'] = async () => manifestData; | ||
const resolvedVersion = await distribution['findPackageForDownload'](input); | ||
expect(resolvedVersion.version).toBe(expected); | ||
}); | ||
|
||
it('version is found but binaries list is empty', async () => { | ||
const distribution = new TemurinDistribution( | ||
{ version: '9.0.8', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot | ||
); | ||
distribution['getAvailableVersions'] = async () => manifestData; | ||
await expect(distribution['findPackageForDownload']('9.0.8')).rejects.toThrowError( | ||
/Could not find satisfied version for SemVer */ | ||
); | ||
}); | ||
|
||
it('version is not found', async () => { | ||
const distribution = new TemurinDistribution( | ||
{ version: '7.x', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot | ||
); | ||
distribution['getAvailableVersions'] = async () => manifestData; | ||
await expect(distribution['findPackageForDownload']('7.x')).rejects.toThrowError( | ||
/Could not find satisfied version for SemVer */ | ||
); | ||
}); | ||
|
||
it('version list is empty', async () => { | ||
const distribution = new TemurinDistribution( | ||
{ version: '8', architecture: 'x64', packageType: 'jdk', checkLatest: false }, | ||
TemurinImplementation.Hotspot | ||
); | ||
distribution['getAvailableVersions'] = async () => []; | ||
await expect(distribution['findPackageForDownload']('8')).rejects.toThrowError( | ||
/Could not find satisfied version for SemVer */ | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import * as core from '@actions/core'; | ||
import * as tc from '@actions/tool-cache'; | ||
|
||
import fs from 'fs'; | ||
import path from 'path'; | ||
import semver from 'semver'; | ||
|
||
import { JavaBase } from '../base-installer'; | ||
import { ITemurinAvailableVersions } from './models'; | ||
import { JavaDownloadRelease, JavaInstallerOptions, JavaInstallerResults } from '../base-models'; | ||
import { extractJdkFile, getDownloadArchiveExtension, isVersionSatisfies } from '../../util'; | ||
|
||
export enum TemurinImplementation { | ||
Hotspot = 'Hotspot' | ||
} | ||
|
||
export class TemurinDistribution extends JavaBase { | ||
constructor( | ||
installerOptions: JavaInstallerOptions, | ||
private readonly jvmImpl: TemurinImplementation | ||
) { | ||
super(`Temurin-${jvmImpl}`, installerOptions); | ||
} | ||
|
||
protected async findPackageForDownload(version: string): Promise<JavaDownloadRelease> { | ||
const availableVersionsRaw = await this.getAvailableVersions(); | ||
const availableVersionsWithBinaries = availableVersionsRaw | ||
.filter(item => item.binaries.length > 0) | ||
.map(item => { | ||
// normalize 17.0.0-beta+33.0.202107301459 to 17.0.0+33.0.202107301459 for earlier access versions | ||
const formattedVersion = this.stable | ||
? item.version_data.semver | ||
: item.version_data.semver.replace('-beta+', '+'); | ||
return { | ||
version: formattedVersion, | ||
url: item.binaries[0].package.link | ||
} as JavaDownloadRelease; | ||
}); | ||
|
||
const satisfiedVersions = availableVersionsWithBinaries | ||
.filter(item => isVersionSatisfies(version, item.version)) | ||
.sort((a, b) => { | ||
return -semver.compareBuild(a.version, b.version); | ||
}); | ||
|
||
const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null; | ||
if (!resolvedFullVersion) { | ||
const availableOptions = availableVersionsWithBinaries.map(item => item.version).join(', '); | ||
const availableOptionsMessage = availableOptions | ||
? `\nAvailable versions: ${availableOptions}` | ||
: ''; | ||
throw new Error( | ||
`Could not find satisfied version for SemVer '${version}'. ${availableOptionsMessage}` | ||
); | ||
} | ||
|
||
return resolvedFullVersion; | ||
} | ||
|
||
protected async downloadTool(javaRelease: JavaDownloadRelease): Promise<JavaInstallerResults> { | ||
let javaPath: string; | ||
let extractedJavaPath: string; | ||
|
||
core.info( | ||
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...` | ||
); | ||
const javaArchivePath = await tc.downloadTool(javaRelease.url); | ||
|
||
core.info(`Extracting Java archive...`); | ||
let extension = getDownloadArchiveExtension(); | ||
|
||
extractedJavaPath = await extractJdkFile(javaArchivePath, extension); | ||
|
||
const archiveName = fs.readdirSync(extractedJavaPath)[0]; | ||
const archivePath = path.join(extractedJavaPath, archiveName); | ||
const version = this.getToolcacheVersionName(javaRelease.version); | ||
|
||
javaPath = await tc.cacheDir(archivePath, this.toolcacheFolderName, version, this.architecture); | ||
|
||
return { version: javaRelease.version, path: javaPath }; | ||
} | ||
|
||
protected get toolcacheFolderName(): string { | ||
return super.toolcacheFolderName; | ||
} | ||
|
||
private async getAvailableVersions(): Promise<ITemurinAvailableVersions[]> { | ||
const platform = this.getPlatformOption(); | ||
const arch = this.architecture; | ||
const imageType = this.packageType; | ||
const versionRange = encodeURI('[1.0,100.0]'); // retrieve all available versions | ||
const releaseType = this.stable ? 'ga' : 'ea'; | ||
|
||
console.time('temurin-retrieve-available-versions'); | ||
|
||
const baseRequestArguments = [ | ||
`project=jdk`, | ||
'vendor=adoptium', | ||
`heap_size=normal`, | ||
'sort_method=DEFAULT', | ||
'sort_order=DESC', | ||
`os=${platform}`, | ||
`architecture=${arch}`, | ||
`image_type=${imageType}`, | ||
`release_type=${releaseType}`, | ||
`jvm_impl=${this.jvmImpl.toLowerCase()}` | ||
].join('&'); | ||
|
||
// need to iterate through all pages to retrieve the list of all versions | ||
// Adoptium API doesn't provide way to retrieve the count of pages to iterate so infinity loop | ||
let page_index = 0; | ||
const availableVersions: ITemurinAvailableVersions[] = []; | ||
while (true) { | ||
const requestArguments = `${baseRequestArguments}&page_size=20&page=${page_index}`; | ||
const availableVersionsUrl = `https://api.adoptium.net/v3/assets/version/${versionRange}?${requestArguments}`; | ||
if (core.isDebug() && page_index === 0) { | ||
// url is identical except page_index so print it once for debug | ||
core.debug(`Gathering available versions from '${availableVersionsUrl}'`); | ||
} | ||
|
||
const paginationPage = ( | ||
await this.http.getJson<ITemurinAvailableVersions[]>(availableVersionsUrl) | ||
).result; | ||
if (paginationPage === null || paginationPage.length === 0) { | ||
// break infinity loop because we have reached end of pagination | ||
break; | ||
} | ||
|
||
availableVersions.push(...paginationPage); | ||
page_index++; | ||
} | ||
|
||
if (core.isDebug()) { | ||
core.startGroup('Print information about available versions'); | ||
console.timeEnd('temurin-retrieve-available-versions'); | ||
console.log(`Available versions: [${availableVersions.length}]`); | ||
console.log(availableVersions.map(item => item.version_data.semver).join(', ')); | ||
core.endGroup(); | ||
} | ||
|
||
return availableVersions; | ||
} | ||
|
||
private getPlatformOption(): string { | ||
// Adoptium has own platform names so need to map them | ||
switch (process.platform) { | ||
case 'darwin': | ||
return 'mac'; | ||
case 'win32': | ||
return 'windows'; | ||
default: | ||
return process.platform; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Models from https://api.adoptium.net/q/swagger-ui/#/Assets/searchReleasesByVersion | ||
|
||
export interface ITemurinAvailableVersions { | ||
binaries: [ | ||
{ | ||
architecture: string; | ||
heap_size: string; | ||
image_type: string; | ||
jvm_impl: string; | ||
os: string; | ||
package: { | ||
checksum: string; | ||
checksum_link: string; | ||
download_count: number; | ||
link: string; | ||
metadata_link: string; | ||
name: string; | ||
size: string; | ||
}; | ||
project: string; | ||
scm_ref: string; | ||
updated_at: string; | ||
} | ||
]; | ||
id: string; | ||
release_link: string; | ||
release_name: string; | ||
release_type: string; | ||
vendor: string; | ||
version_data: { | ||
build: number; | ||
major: number; | ||
minor: number; | ||
openjdk_version: string; | ||
security: string; | ||
semver: string; | ||
}; | ||
} |