Files
setup-java/src/distributions/kona/installer.ts
T
Bruno BorgesandCopilot App 885218c5e4 Move the extracted JDK into the tool-cache and speed up extraction (#1206)
Two wall-clock optimizations on the JDK install path.

`tc.cacheDir` recursively copies the extracted tree into RUNNER_TOOL_CACHE,
so a 200-600MB JDK is written to disk twice. The extraction directory and
the tool-cache normally share a filesystem, so `cacheJdkDir` renames it
instead and writes the `.complete` marker itself, mirroring the destination
layout `tc.cacheDir` produces. It falls back to the copy when the tool-cache
location is unknown, when the source is not a real directory (a symlinked
source would otherwise leave a dangling entry once RUNNER_TEMP is cleaned),
or when the rename fails - a cross-device tool-cache, or anti-virus holding
a handle on Windows. The rename is atomic, so the source is still intact
for the fallback.

Extraction now uses `pigz` for tarballs when the runner provides it, and
Windows zips go through the bundled `tar.exe` rather than `tc.extractZip`,
which shells out to PowerShell's much slower `Expand-Archive`. Both fall
back to the stock extraction and clean up the abandoned directory first.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b76d8cb0-f629-46e1-bf9a-ffde06948644
2026-08-04 23:05:06 -04:00

199 lines
5.2 KiB
TypeScript

import * as core from '@actions/core';
import semver from 'semver';
import fs from 'fs';
import path from 'path';
import {JavaBase} from '../base-installer.js';
import {IKonaReleaseInfo, IKonaRelease} from './models.js';
import {
JavaDownloadRelease,
JavaInstallerOptions,
JavaInstallerResults
} from '../base-models.js';
import {
cacheJdkDir,
extractJdkFile,
getDownloadArchiveExtension,
isVersionSatisfies,
renameWinArchive
} from '../../util.js';
const KONA_RELEASES_URL =
'https://tencent.github.io/konajdk/releases/kona-v1.json';
export class KonaDistribution extends JavaBase {
constructor(installerOptions: JavaInstallerOptions) {
super('Kona', installerOptions);
}
protected async downloadTool(
javaRelease: JavaDownloadRelease
): Promise<JavaInstallerResults> {
core.info(
`Downloading Kona JDK ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
);
const javaArchivePath = await this.downloadAndVerify(javaRelease);
core.info(`Extracting Java archive...`);
const extension = getDownloadArchiveExtension();
const archivePath =
process.platform === 'win32'
? renameWinArchive(javaArchivePath)
: javaArchivePath;
const extractedJavaPath = await extractJdkFile(archivePath, extension);
const archiveName = fs.readdirSync(extractedJavaPath)[0];
const jdkDirectory = path.join(extractedJavaPath, archiveName);
const version = this.getToolcacheVersionName(javaRelease.version);
const javaPath = await cacheJdkDir(
jdkDirectory,
this.toolcacheFolderName,
version,
this.architecture
);
return {version: javaRelease.version, path: javaPath};
}
protected async findPackageForDownload(
version: string
): Promise<JavaDownloadRelease> {
if (!this.stable) {
throw new Error('Kona provides stable releases only');
}
if (this.packageType !== 'jdk') {
throw new Error('Kona provides jdk only');
}
const availableReleases = await this.getAvailableReleases();
const releases = availableReleases
.filter(item => {
return isVersionSatisfies(version, item.version);
})
.map(item => {
return {
version: item.version,
url: item.downloadUrl,
checksum: item.checksum
? {
algorithm: 'sha256',
value: item.checksum,
source: KONA_RELEASES_URL
}
: undefined
} as JavaDownloadRelease;
})
.sort((a, b) => -semver.compareBuild(a.version, b.version));
if (!releases.length) {
throw new Error(
`No Kona release for the specified version "${version}" on OS "${this.getOs()}" and arch "${this.getArch()}".`
);
}
return releases[0];
}
private async getAvailableReleases(): Promise<IKonaRelease[]> {
if (core.isDebug()) {
console.time('Retrieving available releases for Kona took'); // eslint-disable-line no-console
}
const releaseInfo = await this.fetchReleaseInfo();
if (!releaseInfo) {
throw new Error(`Couldn't fetch Kona release information`);
}
const availableReleases = this.chooseReleases(
this.getOs(),
this.getArch(),
releaseInfo
);
if (core.isDebug()) {
core.startGroup('Print information about available releases');
console.timeEnd('Retrieving available releases for Kona took'); // eslint-disable-line no-console
core.debug(availableReleases.map(item => item.version).join(', '));
core.endGroup();
}
return availableReleases;
}
private async fetchReleaseInfo(): Promise<IKonaReleaseInfo | null> {
try {
core.debug(`Fetching Kona release info from URL: ${KONA_RELEASES_URL}`);
return (await this.http.getJson<IKonaReleaseInfo>(KONA_RELEASES_URL))
.result;
} catch (err) {
core.debug(
`Fetching Kona release info from the URL: ${KONA_RELEASES_URL} failed with the error: ${
(err as Error).message
}`
);
return null;
}
}
private chooseReleases(
os: string,
arch: string,
releaseInfo: IKonaReleaseInfo
): IKonaRelease[] {
const releases: IKonaRelease[] = [];
for (const majorVersion in releaseInfo) {
const versions = releaseInfo[majorVersion];
for (const version of versions) {
if (!version.latest) {
continue;
}
for (const file of version.files) {
if (file.os === os && file.arch === arch) {
releases.push({
version: version.version,
jdkVersion: version.jdkVersion,
os: os,
arch: arch,
downloadUrl: version.baseUrl + file.filename,
checksum: file.checksum
});
break;
}
}
}
}
return releases;
}
private getOs(): string {
switch (process.platform) {
case 'darwin':
return 'macos';
case 'win32':
return 'windows';
default:
return process.platform;
}
}
private getArch(): string {
switch (this.architecture) {
case 'arm64':
return 'aarch64';
case 'x64':
return 'x86_64';
default:
return this.architecture;
}
}
}