mirror of
https://github.com/actions/setup-java.git
synced 2026-08-18 19:52:52 +00:00
Add Red Hat Build of OpenJDK support (#1241)
* Add Red Hat Build of OpenJDK support Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix Red Hat tests on Windows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update compiled Red Hat distribution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot App
Copilot Autofix powered by AI
parent
5f75b27283
commit
416c6d1e8a
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"result": [
|
||||
{
|
||||
"filename": "java-21-openjdk-21.0.8.0.9-1.portable.jdk.x86_64.tar.xz",
|
||||
"direct_download_uri": "https://developers.redhat.com/content-gateway/file/pub/openjdk/java-21-openjdk-21.0.8.0.9-1.portable.jdk.x86_64.tar.xz",
|
||||
"checksum": "91a6f3284cbded9de17f65985a4fca48dd4fd0aa295162178631c23ade335aad",
|
||||
"checksum_type": "sha256"
|
||||
}
|
||||
],
|
||||
"message": ""
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"result": [
|
||||
{
|
||||
"id": "redhat-21.0.8-jdk-linux-x64",
|
||||
"archive_type": "tar.xz",
|
||||
"distribution": "redhat",
|
||||
"java_version": "21.0.8+9",
|
||||
"release_status": "ga",
|
||||
"operating_system": "linux",
|
||||
"architecture": "x64",
|
||||
"package_type": "jdk",
|
||||
"directly_downloadable": true
|
||||
},
|
||||
{
|
||||
"id": "redhat-21.0.7-jdk-linux-x64",
|
||||
"archive_type": "tar.xz",
|
||||
"distribution": "redhat",
|
||||
"java_version": "21.0.7+6",
|
||||
"release_status": "ga",
|
||||
"operating_system": "linux",
|
||||
"architecture": "x64",
|
||||
"package_type": "jdk",
|
||||
"directly_downloadable": true
|
||||
},
|
||||
{
|
||||
"id": "redhat-17.0.16-jdk-linux-x64",
|
||||
"archive_type": "tar.xz",
|
||||
"distribution": "redhat",
|
||||
"java_version": "17.0.16+8",
|
||||
"release_status": "ga",
|
||||
"operating_system": "linux",
|
||||
"architecture": "x64",
|
||||
"package_type": "jdk",
|
||||
"directly_downloadable": true
|
||||
},
|
||||
{
|
||||
"id": "redhat-9-jdk-linux-x64",
|
||||
"archive_type": "tar.xz",
|
||||
"distribution": "redhat",
|
||||
"java_version": "9+181",
|
||||
"release_status": "ga",
|
||||
"operating_system": "linux",
|
||||
"architecture": "x64",
|
||||
"package_type": "jdk",
|
||||
"directly_downloadable": true
|
||||
},
|
||||
{
|
||||
"id": "redhat-21.0.8-jre-linux-x64",
|
||||
"archive_type": "tar.xz",
|
||||
"distribution": "redhat",
|
||||
"java_version": "21.0.8+9",
|
||||
"release_status": "ga",
|
||||
"operating_system": "linux",
|
||||
"architecture": "x64",
|
||||
"package_type": "jre",
|
||||
"directly_downloadable": true
|
||||
},
|
||||
{
|
||||
"id": "redhat-17.0.16-jdk-windows-x64",
|
||||
"archive_type": "zip",
|
||||
"distribution": "redhat",
|
||||
"java_version": "17.0.16+8",
|
||||
"release_status": "ga",
|
||||
"operating_system": "windows",
|
||||
"architecture": "x64",
|
||||
"package_type": "jdk",
|
||||
"directly_downloadable": true
|
||||
}
|
||||
],
|
||||
"message": "6 package(s) found"
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import {beforeEach, describe, expect, it, jest} from '@jest/globals';
|
||||
import path from 'path';
|
||||
|
||||
const mockReaddirSync = jest.fn();
|
||||
const realFs = await import('fs');
|
||||
jest.unstable_mockModule('fs', () => ({
|
||||
...realFs,
|
||||
default: {...realFs.default, readdirSync: mockReaddirSync},
|
||||
readdirSync: mockReaddirSync
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
info: jest.fn(),
|
||||
debug: jest.fn(),
|
||||
isDebug: jest.fn(() => false),
|
||||
startGroup: jest.fn(),
|
||||
endGroup: jest.fn()
|
||||
}));
|
||||
|
||||
const realUtil = await import('../../src/util.js');
|
||||
const mockCacheJdkDir = jest.fn();
|
||||
const mockExtractJdkFile = jest.fn();
|
||||
const mockRenameWinArchive = jest.fn();
|
||||
jest.unstable_mockModule('../../src/util.js', () => ({
|
||||
...realUtil,
|
||||
cacheJdkDir: mockCacheJdkDir,
|
||||
extractJdkFile: mockExtractJdkFile,
|
||||
renameWinArchive: mockRenameWinArchive
|
||||
}));
|
||||
|
||||
const {RedHatDistribution} =
|
||||
await import('../../src/distributions/redhat/installer.js');
|
||||
|
||||
const release = {
|
||||
version: '21.0.8+9',
|
||||
url: 'https://developers.redhat.com/openjdk-21.tar.xz',
|
||||
checksum: {
|
||||
algorithm: 'sha256' as const,
|
||||
value: 'a'.repeat(64)
|
||||
}
|
||||
};
|
||||
|
||||
const createDistribution = () =>
|
||||
new RedHatDistribution({
|
||||
version: '21',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockReaddirSync.mockReturnValue(['jdk-21']);
|
||||
mockExtractJdkFile.mockResolvedValue('/tmp/extracted');
|
||||
mockCacheJdkDir.mockResolvedValue('/toolcache/Java_RedHat_jdk/21.0.8-9/x64');
|
||||
mockRenameWinArchive.mockReturnValue('/tmp/download.zip');
|
||||
});
|
||||
|
||||
describe('Red Hat package installation', () => {
|
||||
it('extracts and caches Linux tar.xz archives', async () => {
|
||||
const distribution = createDistribution();
|
||||
distribution['downloadAndVerify'] = jest
|
||||
.fn()
|
||||
.mockResolvedValue('/tmp/download');
|
||||
distribution['getArchiveType'] = () => 'tar.xz';
|
||||
|
||||
const result = await distribution['downloadTool'](release);
|
||||
|
||||
expect(mockExtractJdkFile).toHaveBeenCalledWith('/tmp/download', 'tar.xz');
|
||||
expect(mockRenameWinArchive).not.toHaveBeenCalled();
|
||||
expect(mockCacheJdkDir).toHaveBeenCalledWith(
|
||||
path.join('/tmp/extracted', 'jdk-21'),
|
||||
'Java_RedHat_jdk',
|
||||
'21.0.8-9',
|
||||
'x64'
|
||||
);
|
||||
expect(result).toEqual({
|
||||
version: '21.0.8+9',
|
||||
path: '/toolcache/Java_RedHat_jdk/21.0.8-9/x64'
|
||||
});
|
||||
});
|
||||
|
||||
it('renames downloaded Windows ZIP archives before extraction', async () => {
|
||||
const distribution = createDistribution();
|
||||
distribution['downloadAndVerify'] = jest
|
||||
.fn()
|
||||
.mockResolvedValue('/tmp/download');
|
||||
distribution['getArchiveType'] = () => 'zip';
|
||||
|
||||
await distribution['downloadTool'](release);
|
||||
|
||||
expect(mockRenameWinArchive).toHaveBeenCalledWith('/tmp/download');
|
||||
expect(mockExtractJdkFile).toHaveBeenCalledWith('/tmp/download.zip', 'zip');
|
||||
});
|
||||
|
||||
it('fails when the extracted archive is empty', async () => {
|
||||
mockReaddirSync.mockReturnValue([]);
|
||||
const distribution = createDistribution();
|
||||
distribution['downloadAndVerify'] = jest
|
||||
.fn()
|
||||
.mockResolvedValue('/tmp/download');
|
||||
distribution['getArchiveType'] = () => 'tar.xz';
|
||||
|
||||
await expect(distribution['downloadTool'](release)).rejects.toThrow(
|
||||
'The Red Hat archive for Java 21.0.8+9 was empty.'
|
||||
);
|
||||
expect(mockCacheJdkDir).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
import {afterEach, describe, expect, it, jest} from '@jest/globals';
|
||||
import {HttpClient} from '@actions/http-client';
|
||||
import packageDetails from '../data/redhat-package-details.json' with {type: 'json'};
|
||||
import packages from '../data/redhat-packages.json' with {type: 'json'};
|
||||
import {RedHatDistribution} from '../../src/distributions/redhat/installer.js';
|
||||
|
||||
const createDistribution = (
|
||||
version: string,
|
||||
packageType = 'jdk'
|
||||
): RedHatDistribution => {
|
||||
const distribution = new RedHatDistribution({
|
||||
version,
|
||||
architecture: 'x64',
|
||||
packageType,
|
||||
checkLatest: false
|
||||
});
|
||||
distribution['getOperatingSystem'] = () => 'linux';
|
||||
distribution['getArchiveType'] = () => 'tar.xz';
|
||||
return distribution;
|
||||
};
|
||||
|
||||
const mockDisco = (
|
||||
packageResponse: unknown = packages,
|
||||
detailsResponse: unknown = packageDetails
|
||||
) =>
|
||||
jest.spyOn(HttpClient.prototype, 'getJson').mockImplementation(async url => ({
|
||||
result: url.includes('/packages?') ? packageResponse : detailsResponse,
|
||||
statusCode: 200,
|
||||
headers: {}
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('Red Hat package resolution', () => {
|
||||
it('resolves the newest matching release and authoritative checksum', async () => {
|
||||
const getJson = mockDisco();
|
||||
const distribution = createDistribution('21');
|
||||
|
||||
const release = await distribution['findPackageForDownload']('21');
|
||||
|
||||
expect(release).toEqual({
|
||||
version: '21.0.8+9',
|
||||
url: packageDetails.result[0].direct_download_uri,
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: packageDetails.result[0].checksum,
|
||||
source:
|
||||
'https://api.foojay.io/disco/v3.0/ids/redhat-21.0.8-jdk-linux-x64'
|
||||
}
|
||||
});
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringContaining(
|
||||
'distro=redhat&release_status=ga&operating_system=linux&architecture=x64&package_type=jdk&archive_type=tar.xz&directly_downloadable=true'
|
||||
)
|
||||
);
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'https://api.foojay.io/disco/v3.0/ids/redhat-21.0.8-jdk-linux-x64'
|
||||
);
|
||||
});
|
||||
|
||||
it('normalizes feature-only Disco versions before matching', async () => {
|
||||
mockDisco(undefined, {
|
||||
result: [
|
||||
{
|
||||
...packageDetails.result[0],
|
||||
direct_download_uri: 'https://developers.redhat.com/openjdk-9.tar.xz'
|
||||
}
|
||||
],
|
||||
message: ''
|
||||
});
|
||||
const distribution = createDistribution('9');
|
||||
|
||||
const release = await distribution['findPackageForDownload']('9');
|
||||
|
||||
expect(release.version).toBe('9.0.0+181');
|
||||
});
|
||||
|
||||
it('selects the requested package type', async () => {
|
||||
const getJson = mockDisco();
|
||||
const distribution = createDistribution('21', 'jre');
|
||||
|
||||
await distribution['findPackageForDownload']('21');
|
||||
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringContaining('package_type=jre')
|
||||
);
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'https://api.foojay.io/disco/v3.0/ids/redhat-21.0.8-jre-linux-x64'
|
||||
);
|
||||
});
|
||||
|
||||
it('maps Windows to ZIP packages', async () => {
|
||||
const getJson = mockDisco();
|
||||
const distribution = createDistribution('17');
|
||||
distribution['getOperatingSystem'] = () => 'windows';
|
||||
distribution['getArchiveType'] = () => 'zip';
|
||||
|
||||
await distribution['findPackageForDownload']('17');
|
||||
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.stringContaining(
|
||||
'operating_system=windows&architecture=x64&package_type=jdk&archive_type=zip'
|
||||
)
|
||||
);
|
||||
expect(getJson).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'https://api.foojay.io/disco/v3.0/ids/redhat-17.0.16-jdk-windows-x64'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects Alpine before requesting glibc package metadata', async () => {
|
||||
const getJson = mockDisco();
|
||||
const distribution = new RedHatDistribution({
|
||||
version: '21',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
expect(() => distribution['getOperatingSystem'](true)).toThrow(
|
||||
"Distribution 'redhat' does not support Alpine Linux because Red Hat portable archives require glibc."
|
||||
);
|
||||
expect(getJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects early-access versions before requesting metadata', async () => {
|
||||
const getJson = mockDisco();
|
||||
const distribution = createDistribution('21-ea');
|
||||
|
||||
await expect(distribution['findPackageForDownload']('21')).rejects.toThrow(
|
||||
'Early access versions are not supported'
|
||||
);
|
||||
expect(getJson).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reports available versions when no release matches', async () => {
|
||||
mockDisco();
|
||||
const distribution = createDistribution('25');
|
||||
|
||||
await expect(distribution['findPackageForDownload']('25')).rejects.toThrow(
|
||||
"No matching version found for SemVer '25'.\nDistribution: RedHat"
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when the package detail has no direct Red Hat URL', async () => {
|
||||
mockDisco(packages, {result: [], message: ''});
|
||||
const distribution = createDistribution('21');
|
||||
|
||||
await expect(distribution['findPackageForDownload']('21')).rejects.toThrow(
|
||||
'returned no direct Red Hat download URL'
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when the package detail has no SHA-256 checksum', async () => {
|
||||
mockDisco(packages, {
|
||||
result: [{...packageDetails.result[0], checksum: ''}],
|
||||
message: ''
|
||||
});
|
||||
const distribution = createDistribution('21');
|
||||
|
||||
await expect(distribution['findPackageForDownload']('21')).rejects.toThrow(
|
||||
'returned no SHA-256 checksum'
|
||||
);
|
||||
});
|
||||
|
||||
it('fails with a targeted error when package metadata is missing', async () => {
|
||||
mockDisco(null);
|
||||
const distribution = createDistribution('21');
|
||||
|
||||
await expect(distribution['findPackageForDownload']('21')).rejects.toThrow(
|
||||
'Could not fetch Red Hat package metadata from Foojay Disco'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -405,6 +405,18 @@ describe('extractJdkFile', () => {
|
||||
expect(io.which).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('extracts xz-compressed tarballs with xz tar flags', async () => {
|
||||
(tc.extractTar as jest.Mock).mockResolvedValue('/extracted' as never);
|
||||
|
||||
await expect(extractJdkFile('/tmp/jdk.tar.xz')).resolves.toBe('/extracted');
|
||||
expect(tc.extractTar).toHaveBeenCalledWith(
|
||||
'/tmp/jdk.tar.xz',
|
||||
undefined,
|
||||
'xJ'
|
||||
);
|
||||
expect(io.which).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses the bundled tar.exe for zip archives on Windows', async () => {
|
||||
setPlatform('win32');
|
||||
const systemRoot = path.join(workDir, 'Windows');
|
||||
|
||||
Reference in New Issue
Block a user