Support Temurin JDKs with JMOD files (#1149)

* Initial plan

* Add Temurin JMOD installation support

* Rebuild action bundles

* Use java-package for Temurin JMODs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e7d8c581-2d14-4ccc-aeca-afc0f3b0c2bc

* Fix Temurin JMOD test paths on Windows

Use platform-aware path construction for the JMOD copy and cache assertions so the Windows test expects backslash-normalized paths.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Bruno Borges <brborges@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e7d8c581-2d14-4ccc-aeca-afc0f3b0c2bc
This commit is contained in:
Copilot
2026-07-28 18:39:15 -04:00
committed by GitHub
parent 0b0bd25927
commit 1c3b3d28f0
8 changed files with 244 additions and 53 deletions
@@ -0,0 +1,16 @@
import {getJavaDistribution} from '../../src/distributions/distribution-factory.js';
describe('getJavaDistribution', () => {
it("rejects java-package 'jdk+jmods' for non-Temurin distributions", () => {
expect(() =>
getJavaDistribution('zulu', {
version: '25',
architecture: 'x64',
packageType: 'jdk+jmods',
checkLatest: false
})
).toThrow(
"java-package 'jdk+jmods' is only supported for distribution 'temurin'."
);
});
});
@@ -13,6 +13,7 @@ import type {TemurinImplementation as TemurinImplementationType} from '../../src
import {HttpClient} from '@actions/http-client';
import fs from 'fs';
import os from 'os';
import path from 'path';
import manifestData from '../data/temurin.json' with {type: 'json'};
@@ -119,6 +120,16 @@ describe('getAvailableVersions', () => {
TemurinImplementation.Hotspot,
'os=mac&architecture=x64&image_type=jdk&release_type=ga&jvm_impl=hotspot&page_size=20&page=0'
],
[
{
version: '25',
architecture: 'x64',
packageType: 'jdk+jmods',
checkLatest: false
},
TemurinImplementation.Hotspot,
'os=mac&architecture=x64&image_type=jdk&release_type=ga&jvm_impl=hotspot&page_size=20&page=0'
],
[
{
version: '16',
@@ -169,6 +180,27 @@ describe('getAvailableVersions', () => {
}
);
it('requests the JMOD image type', async () => {
const distribution = new TemurinDistribution(
{
version: '25',
architecture: 'x64',
packageType: 'jdk+jmods',
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';
@@ -229,7 +261,12 @@ describe('getAvailableVersions', () => {
it.each([
[TemurinImplementation.Hotspot, 'jdk', 'Java_Temurin-Hotspot_jdk'],
[TemurinImplementation.Hotspot, 'jre', 'Java_Temurin-Hotspot_jre']
[TemurinImplementation.Hotspot, 'jre', 'Java_Temurin-Hotspot_jre'],
[
TemurinImplementation.Hotspot,
'jdk+jmods',
'Java_Temurin-Hotspot_jdk+jmods'
]
])(
'find right toolchain folder',
(
@@ -386,6 +423,7 @@ describe('downloadTool', () => {
let spyCacheDir: any;
let spyReadDirSync: any;
let spyRenameWinArchive: any;
let spyCopySync: any;
beforeEach(() => {
spyDownloadTool = tc.downloadTool as jest.Mock;
@@ -400,6 +438,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 +473,60 @@ 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+jmods',
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(
path.join('/tmp/extracted-jmods', 'jdk-25-jmods'),
process.platform === 'darwin'
? path.join('/tmp/extracted', 'jdk-25', 'Contents', 'Home', 'jmods')
: path.join('/tmp/extracted', 'jdk-25', 'jmods'),
{recursive: true}
);
expect(spyCacheDir).toHaveBeenCalledWith(
path.join('/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(
{