setup-java/src/distributions/zulu/installer.ts
2026-04-17 19:14:02 +09:00

242 lines
7.3 KiB
TypeScript

import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import path from 'path';
import fs from 'fs';
import semver from 'semver';
import {JavaBase} from '../base-installer';
import {IZuluVersions} from './models';
import {
extractJdkFile,
getDownloadArchiveExtension,
convertVersionToSemver,
isVersionSatisfies,
renameWinArchive
} from '../../util';
import {
JavaDownloadRelease,
JavaInstallerOptions,
JavaInstallerResults
} from '../base-models';
export class ZuluDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
super('Zulu', installerOptions);
}
protected async findPackageForDownload(
version: string
): Promise<JavaDownloadRelease> {
const availableVersionsRaw = await this.getAvailableVersions();
const availableVersions = availableVersionsRaw.map(item => {
return {
version: convertVersionToSemver(item.java_version),
url: item.download_url,
zuluVersion: convertVersionToSemver(item.distro_version)
};
});
const satisfiedVersions = availableVersions
.filter(item => isVersionSatisfies(version, item.version))
.sort((a, b) => {
// Azul provides two versions: java_version and distro_version
// we should sort by both fields by descending
return (
-semver.compareBuild(a.version, b.version) ||
-semver.compareBuild(a.zuluVersion, b.zuluVersion)
);
})
.map(item => {
return {
version: item.version,
url: item.url
} as JavaDownloadRelease;
});
const resolvedFullVersion =
satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
if (!resolvedFullVersion) {
const availableVersionStrings = availableVersions.map(
item => item.version
);
throw this.createVersionNotFoundError(version, availableVersionStrings);
}
return resolvedFullVersion;
}
protected async downloadTool(
javaRelease: JavaDownloadRelease
): Promise<JavaInstallerResults> {
core.info(
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
);
let javaArchivePath = await tc.downloadTool(javaRelease.url);
core.info(`Extracting Java archive...`);
const extension = getDownloadArchiveExtension();
if (process.platform === 'win32') {
javaArchivePath = renameWinArchive(javaArchivePath);
}
const extractedJavaPath = await extractJdkFile(javaArchivePath, extension);
const archiveName = fs.readdirSync(extractedJavaPath)[0];
const archivePath = path.join(extractedJavaPath, archiveName);
const javaPath = await tc.cacheDir(
archivePath,
this.toolcacheFolderName,
this.getToolcacheVersionName(javaRelease.version),
this.architecture
);
return {version: javaRelease.version, path: javaPath};
}
private async getAvailableVersions(): Promise<IZuluVersions[]> {
const {arch, hw_bitness} = this.getArchitectureOptions();
const [bundleType, features] = this.packageType.split('+');
const platform = this.getPlatformOption();
const extension = getDownloadArchiveExtension();
const javafx = features?.includes('fx') ?? false;
const crac = features?.includes('crac') ?? false;
const releaseStatus = this.stable ? 'ga' : 'ea';
if (core.isDebug()) {
console.time('Retrieving available versions for Zulu took'); // eslint-disable-line no-console
}
// Map old API parameters to new metadata API parameters
const osParam = this.getOsParam(platform);
const archiveType = this.getArchiveType(extension);
const archParam = this.getArchParam(arch, hw_bitness);
// Fetch all pages to avoid missing packages when there are > 100 results
let allVersions: IZuluVersions[] = [];
let page = 1;
const pageSize = 100;
let hasMore = true;
while (hasMore) {
const requestArguments = [
`os=${osParam}`,
`arch=${archParam}`,
`archive_type=${archiveType}`,
`java_package_type=${bundleType}`,
`javafx_bundled=${javafx}`,
`crac_supported=${crac}`,
`release_status=${releaseStatus}`,
`availability_types=ca`,
// Only filter by TCK certification for GA releases
// EA releases typically don't have TCK certification
releaseStatus === 'ga' ? `certifications=tck` : '',
`page=${page}`,
`page_size=${pageSize}`
]
.filter(Boolean)
.join('&');
const availableVersionsUrl = `https://api.azul.com/metadata/v1/zulu/packages/?${requestArguments}`;
core.debug(`Gathering available versions from '${availableVersionsUrl}'`);
const pageResults =
(await this.http.getJson<Array<IZuluVersions>>(availableVersionsUrl))
.result ?? [];
allVersions = allVersions.concat(pageResults);
// If we got fewer results than page size, we've reached the end
hasMore = pageResults.length === pageSize;
page++;
}
if (core.isDebug()) {
core.startGroup('Print information about available versions');
console.timeEnd('Retrieving available versions for Zulu took'); // eslint-disable-line no-console
core.debug(`Available versions: [${allVersions.length}]`);
core.debug(
allVersions.map(item => item.java_version.join('.')).join(', ')
);
core.endGroup();
}
return allVersions;
}
private getArchitectureOptions(): {
arch: string;
hw_bitness: string;
abi: string;
} {
const arch = this.distributionArchitecture();
switch (arch) {
case 'x64':
return {arch: 'x86', hw_bitness: '64', abi: ''};
case 'x86':
return {arch: 'x86', hw_bitness: '32', abi: ''};
case 'aarch64':
case 'arm64':
return {arch: 'arm', hw_bitness: '64', abi: ''};
default:
return {arch: arch, hw_bitness: '', abi: ''};
}
}
private getPlatformOption(): string {
// Azul has own platform names so need to map them
switch (process.platform) {
case 'darwin':
return 'macos';
case 'win32':
return 'windows';
default:
return process.platform;
}
}
private getOsParam(platform: string): string {
// Map platform to new metadata API OS parameter
// The new API uses more specific OS names like 'linux-glibc', 'macos', 'windows'
switch (platform) {
case 'linux':
return 'linux-glibc';
case 'macos':
return 'macos';
case 'windows':
return 'windows';
default:
return platform;
}
}
private getArchParam(arch: string, hw_bitness: string): string {
// Map architecture to new metadata API arch parameter
// The new API uses x64, x86, aarch64, arm
if (arch === 'x86' && hw_bitness === '64') {
return 'x64';
} else if (arch === 'x86' && hw_bitness === '32') {
return 'x86';
} else if (arch === 'arm' && hw_bitness === '64') {
return 'aarch64';
} else if (arch === 'arm' && hw_bitness === '') {
return 'arm';
}
// Fallback for other architectures
return arch;
}
private getArchiveType(extension: string): string {
// Map extension to archive_type parameter for new API
switch (extension) {
case 'tar.gz':
return 'tar.gz';
case 'zip':
return 'zip';
default:
return extension;
}
}
}