mirror of
https://github.com/actions/setup-java.git
synced 2026-08-06 17:12:58 +00:00
4fbd0bd19d
On Alpine, `getPlatformOption()` returned the glibc platform key for Dragonwell, Corretto, Zulu, Liberica and Liberica NIK, so the action resolved and installed a glibc JDK that cannot run under musl. Add a shared `isAlpineLinux()` helper and use it to select each vendor's musl artifacts: | distribution | glibc | musl | | ------------ | ------------- | -------------- | | Dragonwell | `linux` | `alpine-linux` | | Corretto | `linux` | `alpine` | | Zulu | `linux_glibc` | `linux_musl` | | Liberica | `linux` | `linux-musl` | | Liberica NIK | `linux` | `linux-musl` | Each key was verified against the vendor's live metadata API or manifest. There is deliberately no silent fallback to glibc when a vendor has no musl build for the requested version or architecture: the existing "could not find a version that satisfies" error fires instead. This matches the behaviour Temurin and SapMachine already have, and a glibc JDK would not run on musl anyway. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 74248bb0-72af-41d8-b85d-b0f5836e68db
397 lines
12 KiB
TypeScript
397 lines
12 KiB
TypeScript
import {
|
|
jest,
|
|
describe,
|
|
it,
|
|
expect,
|
|
beforeEach,
|
|
afterEach,
|
|
beforeAll,
|
|
afterAll
|
|
} from '@jest/globals';
|
|
import fs from 'fs';
|
|
import type {IZuluVersions} from '../../src/distributions/zulu/models.js';
|
|
import {HttpClient} from '@actions/http-client';
|
|
import os from 'os';
|
|
|
|
import manifestData from '../data/zulu-releases-default.json' with {type: 'json'};
|
|
|
|
// Mock @actions/core before importing source modules that depend on it
|
|
jest.unstable_mockModule('@actions/core', () => ({
|
|
info: jest.fn(),
|
|
warning: jest.fn(),
|
|
debug: jest.fn(),
|
|
error: jest.fn(),
|
|
notice: jest.fn(),
|
|
setFailed: jest.fn(),
|
|
setOutput: jest.fn(),
|
|
getInput: jest.fn(),
|
|
getBooleanInput: jest.fn(),
|
|
getMultilineInput: jest.fn(),
|
|
addPath: jest.fn(),
|
|
exportVariable: jest.fn(),
|
|
saveState: jest.fn(),
|
|
getState: jest.fn(),
|
|
setSecret: jest.fn(),
|
|
isDebug: jest.fn(() => false),
|
|
startGroup: jest.fn(),
|
|
endGroup: jest.fn(),
|
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
|
toPlatformPath: jest.fn((p: string) => p),
|
|
toWin32Path: jest.fn((p: string) => p),
|
|
toPosixPath: jest.fn((p: string) => p)
|
|
}));
|
|
|
|
const real_util_module = await import('../../src/util.js');
|
|
jest.unstable_mockModule('../../src/util.js', () => ({
|
|
...real_util_module,
|
|
getDownloadArchiveExtension: jest.fn()
|
|
}));
|
|
|
|
// Dynamic imports after mocking
|
|
const core = await import('@actions/core');
|
|
const {ZuluDistribution} =
|
|
await import('../../src/distributions/zulu/installer.js');
|
|
const utils = await import('../../src/util.js');
|
|
|
|
describe('getAvailableVersions', () => {
|
|
let spyHttpClient: any;
|
|
let spyCoreError: any;
|
|
|
|
beforeEach(() => {
|
|
spyHttpClient = jest.spyOn(HttpClient.prototype, 'getJson');
|
|
spyHttpClient.mockReturnValue({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: [] as IZuluVersions[]
|
|
});
|
|
|
|
(utils.getDownloadArchiveExtension as jest.Mock<any>).mockReturnValue(
|
|
'tar.gz'
|
|
);
|
|
|
|
// Mock core.error to suppress error logs
|
|
spyCoreError = core.error as jest.Mock;
|
|
spyCoreError.mockImplementation(() => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.resetAllMocks();
|
|
jest.clearAllMocks();
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it.each([
|
|
[
|
|
{
|
|
version: '11',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '11-ea',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=i686&release_status=ea&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jre',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jre&javafx_bundled=false&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jdk+fx',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jre+fx',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jre&javafx_bundled=true&crac_supported=false&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '8',
|
|
architecture: 'x64',
|
|
packageType: 'jdk+crac',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=true&arch=x64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '11',
|
|
architecture: 'arm64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=aarch64&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
],
|
|
[
|
|
{
|
|
version: '11',
|
|
architecture: 'arm',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
},
|
|
'?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=arm&release_status=ga&availability_types=ca&page=1&page_size=100'
|
|
]
|
|
])('build correct url for %s -> %s', async (input, parsedUrl) => {
|
|
const distribution = new ZuluDistribution(input);
|
|
distribution['getPlatformOption'] = () => 'macos';
|
|
const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/${parsedUrl}`;
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient.mock.calls).toHaveLength(1);
|
|
expect(spyHttpClient.mock.calls[0][0]).toBe(buildUrl);
|
|
});
|
|
|
|
it.each([
|
|
['amd64', 'x64'],
|
|
['arm64', 'aarch64']
|
|
])(
|
|
'defaults to os.arch(): %s mapped to distro arch: %s',
|
|
async (osArch: string, distroArch: string) => {
|
|
jest
|
|
.spyOn(os, 'arch')
|
|
.mockReturnValue(osArch as ReturnType<typeof os.arch>);
|
|
|
|
const distribution = new ZuluDistribution({
|
|
version: '17',
|
|
architecture: '', // to get default value
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getPlatformOption'] = () => 'macos';
|
|
const buildUrl = `https://api.azul.com/metadata/v1/zulu/packages/?os=macos&archive_type=tar.gz&java_package_type=jdk&javafx_bundled=false&crac_supported=false&arch=${distroArch}&release_status=ga&availability_types=ca&page=1&page_size=100`;
|
|
|
|
await distribution['getAvailableVersions']();
|
|
|
|
expect(spyHttpClient.mock.calls).toHaveLength(1);
|
|
expect(spyHttpClient.mock.calls[0][0]).toBe(buildUrl);
|
|
}
|
|
);
|
|
|
|
it('load available versions', async () => {
|
|
spyHttpClient
|
|
.mockReturnValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: manifestData as IZuluVersions[]
|
|
})
|
|
.mockReturnValueOnce({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: [] as IZuluVersions[]
|
|
});
|
|
|
|
const distribution = new ZuluDistribution({
|
|
version: '11',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
const availableVersions = await distribution['getAvailableVersions']();
|
|
expect(availableVersions).toHaveLength(manifestData.length);
|
|
});
|
|
});
|
|
|
|
describe('getArchitectureOptions', () => {
|
|
it.each([
|
|
[{architecture: 'x64'}, 'x64'],
|
|
[{architecture: 'x86'}, 'i686'],
|
|
[{architecture: 'aarch64'}, 'aarch64'],
|
|
[{architecture: 'arm64'}, 'aarch64'],
|
|
[{architecture: 'arm'}, 'arm']
|
|
])('%s -> %s', (input, expected) => {
|
|
const distribution = new ZuluDistribution({
|
|
version: '11',
|
|
architecture: input.architecture,
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
expect(distribution['getArchitectureOptions']()).toBe(expected);
|
|
});
|
|
});
|
|
|
|
describe('findPackageForDownload', () => {
|
|
let spyPackageDetails: any;
|
|
|
|
const ZULU_CHECKSUM = 'a'.repeat(64);
|
|
|
|
beforeEach(() => {
|
|
// The resolved winning package fetches sha256_hash from the Azul
|
|
// package-details endpoint; stub it so tests never reach the real
|
|
// network.
|
|
spyPackageDetails = jest.spyOn(HttpClient.prototype, 'getJson');
|
|
spyPackageDetails.mockResolvedValue({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: {sha256_hash: ZULU_CHECKSUM}
|
|
});
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it.each([
|
|
['8', '8.0.282+8'],
|
|
['11.x', '11.0.10+9'],
|
|
['8.0', '8.0.282+8'],
|
|
['11.0.x', '11.0.10+9'],
|
|
['15', '15.0.2+7'],
|
|
['9.0.0', '9.0.0+0'],
|
|
['9.0', '9.0.1+0'],
|
|
['8.0.262', '8.0.262+19'], // validate correct choice between [8.0.262.17, 8.0.262.19, 8.0.262.18]
|
|
['8.0.262+17', '8.0.262+17'],
|
|
['15.0.1+8', '15.0.1+8'],
|
|
['15.0.1+9', '15.0.1+9']
|
|
])('version is %s -> %s', async (input, expected) => {
|
|
const distribution = new ZuluDistribution({
|
|
version: input,
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData;
|
|
const result = await distribution['findPackageForDownload'](
|
|
distribution['version']
|
|
);
|
|
expect(result.version).toBe(expected);
|
|
});
|
|
|
|
it('select correct bundle if there are multiple items with the same jdk version but different zulu versions', async () => {
|
|
const distribution = new ZuluDistribution({
|
|
version: '',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData;
|
|
const result = await distribution['findPackageForDownload']('11.0.5');
|
|
expect(result.url).toBe(
|
|
'https://cdn.azul.com/zulu/bin/zulu11.35.15-ca-jdk11.0.5-macosx_x64.tar.gz'
|
|
);
|
|
expect(result.checksum).toEqual({
|
|
algorithm: 'sha256',
|
|
value: ZULU_CHECKSUM,
|
|
source: 'https://api.azul.com/metadata/v1/zulu/packages/test-uuid-10933'
|
|
});
|
|
// Only the winning package's UUID triggers a details request.
|
|
expect(spyPackageDetails).toHaveBeenCalledWith(
|
|
'https://api.azul.com/metadata/v1/zulu/packages/test-uuid-10933'
|
|
);
|
|
expect(spyPackageDetails).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('skips checksum verification when sha256_hash is missing or malformed', async () => {
|
|
spyPackageDetails.mockResolvedValue({
|
|
statusCode: 200,
|
|
headers: {},
|
|
result: {sha256_hash: 'not-a-valid-digest'}
|
|
});
|
|
|
|
const distribution = new ZuluDistribution({
|
|
version: '',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData;
|
|
const result = await distribution['findPackageForDownload']('11.0.5');
|
|
|
|
expect(result.checksum).toBeUndefined();
|
|
expect(core.debug).toHaveBeenCalledWith(
|
|
expect.stringContaining('No authoritative sha256 checksum')
|
|
);
|
|
});
|
|
|
|
it('should throw an error', async () => {
|
|
const distribution = new ZuluDistribution({
|
|
version: '18',
|
|
architecture: 'x86',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
distribution['getAvailableVersions'] = async () => manifestData;
|
|
await expect(
|
|
distribution['findPackageForDownload'](distribution['version'])
|
|
).rejects.toThrow(/No matching version found for SemVer/);
|
|
});
|
|
});
|
|
|
|
describe('Zulu getPlatformOption libc selection', () => {
|
|
const originalPlatform = Object.getOwnPropertyDescriptor(
|
|
process,
|
|
'platform'
|
|
) as PropertyDescriptor;
|
|
|
|
const setPlatform = (platform: NodeJS.Platform) =>
|
|
Object.defineProperty(process, 'platform', {
|
|
...originalPlatform,
|
|
value: platform
|
|
});
|
|
|
|
const distribution = new ZuluDistribution({
|
|
version: '21',
|
|
architecture: 'x64',
|
|
packageType: 'jdk',
|
|
checkLatest: false
|
|
});
|
|
|
|
afterEach(() => {
|
|
Object.defineProperty(process, 'platform', originalPlatform);
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
it('selects the musl artifacts on Alpine', () => {
|
|
setPlatform('linux');
|
|
jest.spyOn(fs, 'existsSync').mockReturnValue(true);
|
|
|
|
expect(distribution['getPlatformOption']()).toBe('linux_musl');
|
|
});
|
|
|
|
it('selects the glibc artifacts on other Linux runners', () => {
|
|
setPlatform('linux');
|
|
jest.spyOn(fs, 'existsSync').mockReturnValue(false);
|
|
|
|
expect(distribution['getPlatformOption']()).toBe('linux_glibc');
|
|
});
|
|
|
|
it('does not probe for Alpine off Linux', () => {
|
|
setPlatform('win32');
|
|
const existsSync = jest.spyOn(fs, 'existsSync');
|
|
|
|
expect(distribution['getPlatformOption']()).toBe('windows');
|
|
expect(existsSync).not.toHaveBeenCalled();
|
|
});
|
|
});
|