mirror of
https://github.com/actions/setup-java.git
synced 2026-07-28 21:06:27 +00:00
Add Temurin JMOD installation support
This commit is contained in:
committed by
GitHub
parent
98b0c8a4ef
commit
7f1a95c821
@@ -169,6 +169,28 @@ describe('getAvailableVersions', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it('requests the JMOD image type', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '25',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
jmod: true,
|
||||
checkLatest: false
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
distribution['getPlatformOption'] = () => 'linux';
|
||||
|
||||
await distribution['getAvailableVersions']('jmods');
|
||||
|
||||
expect(spyHttpClient).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'os=linux&architecture=x64&image_type=jmods&release_type=ga'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('load available versions', async () => {
|
||||
const nextPageUrl =
|
||||
'https://api.adoptium.net/v3/assets/version/%5B1.0,100.0%5D?page=1&page_size=20';
|
||||
@@ -228,13 +250,20 @@ describe('getAvailableVersions', () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
[TemurinImplementation.Hotspot, 'jdk', 'Java_Temurin-Hotspot_jdk'],
|
||||
[TemurinImplementation.Hotspot, 'jre', 'Java_Temurin-Hotspot_jre']
|
||||
[TemurinImplementation.Hotspot, 'jdk', false, 'Java_Temurin-Hotspot_jdk'],
|
||||
[TemurinImplementation.Hotspot, 'jre', false, 'Java_Temurin-Hotspot_jre'],
|
||||
[
|
||||
TemurinImplementation.Hotspot,
|
||||
'jdk',
|
||||
true,
|
||||
'Java_Temurin-Hotspot_jdk_jmods'
|
||||
]
|
||||
])(
|
||||
'find right toolchain folder',
|
||||
(
|
||||
impl: TemurinImplementationType,
|
||||
packageType: string,
|
||||
jmod: boolean,
|
||||
expected: string
|
||||
) => {
|
||||
const distribution = new TemurinDistribution(
|
||||
@@ -242,6 +271,7 @@ describe('getAvailableVersions', () => {
|
||||
version: '8',
|
||||
architecture: 'x64',
|
||||
packageType: packageType,
|
||||
jmod,
|
||||
checkLatest: false
|
||||
},
|
||||
impl
|
||||
@@ -252,6 +282,24 @@ describe('getAvailableVersions', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it('rejects JMODs with a non-JDK package', () => {
|
||||
expect(
|
||||
() =>
|
||||
new TemurinDistribution(
|
||||
{
|
||||
version: '25',
|
||||
architecture: 'x64',
|
||||
packageType: 'jre',
|
||||
jmod: true,
|
||||
checkLatest: false
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
)
|
||||
).toThrow(
|
||||
"Input 'jmod' is only supported with Temurin java-package 'jdk'."
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['amd64', 'x64'],
|
||||
['arm64', 'aarch64']
|
||||
@@ -386,6 +434,7 @@ describe('downloadTool', () => {
|
||||
let spyCacheDir: any;
|
||||
let spyReadDirSync: any;
|
||||
let spyRenameWinArchive: any;
|
||||
let spyCopySync: any;
|
||||
|
||||
beforeEach(() => {
|
||||
spyDownloadTool = tc.downloadTool as jest.Mock;
|
||||
@@ -400,6 +449,8 @@ describe('downloadTool', () => {
|
||||
spyReadDirSync.mockReturnValue(['jdk-17'] as any);
|
||||
spyRenameWinArchive = util.renameWinArchive as jest.Mock;
|
||||
spyRenameWinArchive.mockReturnValue('/tmp/jdk.tar.gz.zip');
|
||||
spyCopySync = jest.spyOn(fs, 'cpSync');
|
||||
spyCopySync.mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -433,6 +484,59 @@ describe('downloadTool', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('downloads and adds matching JMODs to the JDK', async () => {
|
||||
spyDownloadTool
|
||||
.mockResolvedValueOnce('/tmp/jdk.tar.gz')
|
||||
.mockResolvedValueOnce('/tmp/jmods.tar.gz');
|
||||
spyExtractJdkFile
|
||||
.mockResolvedValueOnce('/tmp/extracted')
|
||||
.mockResolvedValueOnce('/tmp/extracted-jmods');
|
||||
spyReadDirSync
|
||||
.mockReturnValueOnce(['jdk-25'] as any)
|
||||
.mockReturnValueOnce(['jdk-25-jmods'] as any);
|
||||
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
||||
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '25',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
jmod: true,
|
||||
checkLatest: false
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
distribution['resolvePackage'] = jest.fn().mockResolvedValue({
|
||||
version: '25.0.3+9',
|
||||
url: 'https://example.com/jmods.tar.gz'
|
||||
});
|
||||
|
||||
await distribution['downloadTool']({
|
||||
version: '25.0.3+9',
|
||||
url: 'https://example.com/jdk.tar.gz'
|
||||
});
|
||||
|
||||
expect(distribution['resolvePackage']).toHaveBeenCalledWith(
|
||||
'25.0.3+9',
|
||||
'jmods'
|
||||
);
|
||||
expect(spyDownloadTool).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'https://example.com/jmods.tar.gz'
|
||||
);
|
||||
expect(spyCopySync).toHaveBeenCalledWith(
|
||||
'/tmp/extracted-jmods/jdk-25-jmods',
|
||||
'/tmp/extracted/jdk-25/jmods',
|
||||
{recursive: true}
|
||||
);
|
||||
expect(spyCacheDir).toHaveBeenCalledWith(
|
||||
'/tmp/extracted/jdk-25',
|
||||
'Java_Temurin-Hotspot_jdk_jmods',
|
||||
'25.0.3-9',
|
||||
'x64'
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when signature is missing and verification is enabled', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
|
||||
@@ -16,6 +16,10 @@ inputs:
|
||||
description: 'The package type (jdk, jre, jdk+fx, jre+fx, jdk+crac, jre+crac)'
|
||||
required: false
|
||||
default: 'jdk'
|
||||
jmod:
|
||||
description: 'Set this option to true to include JMOD files with Temurin JDK 24 and later'
|
||||
required: false
|
||||
default: false
|
||||
architecture:
|
||||
description: "The architecture of the package (defaults to the action runner's architecture)"
|
||||
required: false
|
||||
|
||||
@@ -44,6 +44,7 @@ steps:
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '25'
|
||||
jmod: true # optional, includes JMOD files with JDK 24 and later
|
||||
- run: java --version
|
||||
```
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ export const INPUT_JAVA_VERSION = 'java-version';
|
||||
export const INPUT_JAVA_VERSION_FILE = 'java-version-file';
|
||||
export const INPUT_ARCHITECTURE = 'architecture';
|
||||
export const INPUT_JAVA_PACKAGE = 'java-package';
|
||||
export const INPUT_JMOD = 'jmod';
|
||||
export const INPUT_DISTRIBUTION = 'distribution';
|
||||
export const INPUT_JDK_FILE = 'jdk-file';
|
||||
export const INPUT_JDK_FILE_DEPRECATED = 'jdkFile';
|
||||
|
||||
@@ -2,6 +2,7 @@ export interface JavaInstallerOptions {
|
||||
version: string;
|
||||
architecture: string;
|
||||
packageType: string;
|
||||
jmod?: boolean;
|
||||
checkLatest: boolean;
|
||||
forceDownload?: boolean;
|
||||
setDefault?: boolean;
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as gpg from '../../gpg.js';
|
||||
import {ADOPTIUM_PUBLIC_KEY} from './adoptium-key.js';
|
||||
import {JavaBase} from '../base-installer.js';
|
||||
import {ITemurinAvailableVersions} from './models.js';
|
||||
import {MACOS_JAVA_CONTENT_POSTFIX} from '../../constants.js';
|
||||
import {
|
||||
JavaDownloadRelease,
|
||||
JavaInstallerOptions,
|
||||
@@ -31,11 +32,19 @@ export enum TemurinImplementation {
|
||||
}
|
||||
|
||||
export class TemurinDistribution extends JavaBase {
|
||||
private readonly jmod: boolean;
|
||||
|
||||
constructor(
|
||||
installerOptions: JavaInstallerOptions,
|
||||
private readonly jvmImpl: TemurinImplementation
|
||||
) {
|
||||
super(`Temurin-${jvmImpl}`, installerOptions);
|
||||
this.jmod = installerOptions.jmod ?? false;
|
||||
if (this.jmod && this.packageType !== 'jdk') {
|
||||
throw new Error(
|
||||
`Input 'jmod' is only supported with Temurin java-package 'jdk'.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +53,14 @@ export class TemurinDistribution extends JavaBase {
|
||||
public async findPackageForDownload(
|
||||
version: string
|
||||
): Promise<JavaDownloadRelease> {
|
||||
const availableVersionsRaw = await this.getAvailableVersions();
|
||||
return this.resolvePackage(version, this.packageType);
|
||||
}
|
||||
|
||||
private async resolvePackage(
|
||||
version: string,
|
||||
imageType: string
|
||||
): Promise<JavaDownloadRelease> {
|
||||
const availableVersionsRaw = await this.getAvailableVersions(imageType);
|
||||
const availableVersionsWithBinaries = availableVersionsRaw
|
||||
.filter(item => item.binaries.length > 0)
|
||||
.map(item => {
|
||||
@@ -83,30 +99,7 @@ export class TemurinDistribution extends JavaBase {
|
||||
core.info(
|
||||
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
|
||||
);
|
||||
let javaArchivePath = await tc.downloadTool(javaRelease.url);
|
||||
|
||||
if (this.verifySignature) {
|
||||
if (!javaRelease.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${javaRelease.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
javaArchivePath,
|
||||
javaRelease.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? ADOPTIUM_PUBLIC_KEY
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to verify signature for Temurin version ${javaRelease.version} from ${javaRelease.signatureUrl}: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
{cause: error}
|
||||
);
|
||||
}
|
||||
}
|
||||
let javaArchivePath = await this.downloadPackage(javaRelease);
|
||||
|
||||
core.info(`Extracting Java archive...`);
|
||||
const extension = getDownloadArchiveExtension();
|
||||
@@ -117,6 +110,13 @@ export class TemurinDistribution extends JavaBase {
|
||||
|
||||
const archiveName = fs.readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = path.join(extractedJavaPath, archiveName);
|
||||
const javaHome =
|
||||
process.platform === 'darwin'
|
||||
? path.join(archivePath, MACOS_JAVA_CONTENT_POSTFIX)
|
||||
: archivePath;
|
||||
if (this.jmod && !fs.existsSync(path.join(javaHome, 'jmods'))) {
|
||||
await this.installJmods(javaRelease.version, javaHome);
|
||||
}
|
||||
const version = this.getToolcacheVersionName(javaRelease.version);
|
||||
|
||||
const javaPath = await tc.cacheDir(
|
||||
@@ -130,17 +130,67 @@ export class TemurinDistribution extends JavaBase {
|
||||
}
|
||||
|
||||
protected get toolcacheFolderName(): string {
|
||||
return super.toolcacheFolderName;
|
||||
return `${super.toolcacheFolderName}${this.jmod ? '_jmods' : ''}`;
|
||||
}
|
||||
|
||||
protected supportsSignatureVerification(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async getAvailableVersions(): Promise<ITemurinAvailableVersions[]> {
|
||||
private async downloadPackage(release: JavaDownloadRelease): Promise<string> {
|
||||
const archivePath = await tc.downloadTool(release.url);
|
||||
|
||||
if (this.verifySignature) {
|
||||
if (!release.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${release.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
archivePath,
|
||||
release.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? ADOPTIUM_PUBLIC_KEY
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to verify signature for Temurin version ${release.version} from ${release.signatureUrl}: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
{cause: error}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return archivePath;
|
||||
}
|
||||
|
||||
private async installJmods(version: string, javaHome: string): Promise<void> {
|
||||
const jmodsRelease = await this.resolvePackage(version, 'jmods');
|
||||
core.info(
|
||||
`Downloading JMODs ${jmodsRelease.version} (${this.distribution}) from ${jmodsRelease.url} ...`
|
||||
);
|
||||
let jmodsArchivePath = await this.downloadPackage(jmodsRelease);
|
||||
if (process.platform === 'win32') {
|
||||
jmodsArchivePath = renameWinArchive(jmodsArchivePath);
|
||||
}
|
||||
const extractedJmodsPath = await extractJdkFile(
|
||||
jmodsArchivePath,
|
||||
getDownloadArchiveExtension()
|
||||
);
|
||||
const jmodsDirectory = path.join(
|
||||
extractedJmodsPath,
|
||||
fs.readdirSync(extractedJmodsPath)[0]
|
||||
);
|
||||
fs.cpSync(jmodsDirectory, path.join(javaHome, 'jmods'), {recursive: true});
|
||||
}
|
||||
|
||||
private async getAvailableVersions(
|
||||
imageType = this.packageType
|
||||
): Promise<ITemurinAvailableVersions[]> {
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.distributionArchitecture();
|
||||
const imageType = this.packageType;
|
||||
const versionRange = encodeURI('[1.0,100.0]'); // retrieve all available versions
|
||||
const releaseType = this.stable ? 'ga' : 'ea';
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ async function run() {
|
||||
const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE);
|
||||
const architecture = core.getInput(constants.INPUT_ARCHITECTURE);
|
||||
const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE);
|
||||
const jmod = getBooleanInput(constants.INPUT_JMOD, false);
|
||||
const jdkFile = getJdkFileInput();
|
||||
const cache = core.getInput(constants.INPUT_CACHE);
|
||||
const cacheDependencyPath = core.getInput(
|
||||
@@ -86,6 +87,7 @@ async function run() {
|
||||
const installerInputsOptions: installerInputsOptions = {
|
||||
architecture,
|
||||
packageType,
|
||||
jmod,
|
||||
checkLatest,
|
||||
forceDownload,
|
||||
setDefault,
|
||||
@@ -106,6 +108,7 @@ async function run() {
|
||||
const installerInputsOptions: installerInputsOptions = {
|
||||
architecture,
|
||||
packageType,
|
||||
jmod,
|
||||
checkLatest,
|
||||
forceDownload,
|
||||
setDefault,
|
||||
@@ -164,6 +167,7 @@ async function installVersion(
|
||||
jdkFile,
|
||||
architecture,
|
||||
packageType,
|
||||
jmod,
|
||||
checkLatest,
|
||||
forceDownload,
|
||||
setDefault,
|
||||
@@ -175,6 +179,7 @@ async function installVersion(
|
||||
const installerOptions: JavaInstallerOptions = {
|
||||
architecture,
|
||||
packageType,
|
||||
jmod,
|
||||
checkLatest,
|
||||
forceDownload,
|
||||
setDefault,
|
||||
@@ -219,6 +224,7 @@ async function installVersion(
|
||||
interface installerInputsOptions {
|
||||
architecture: string;
|
||||
packageType: string;
|
||||
jmod: boolean;
|
||||
checkLatest: boolean;
|
||||
forceDownload: boolean;
|
||||
setDefault: boolean;
|
||||
|
||||
Reference in New Issue
Block a user