mirror of
https://github.com/actions/setup-java.git
synced 2026-07-31 22:51:39 +08:00
* Verify JDK downloads with vendor checksums Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Handle missing vendor checksum values Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Preserve checksum error during cleanup failure Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Validate checksum metadata value types Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Clarify checksum documentation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Expand vendor checksum verification Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Accept SHA-256 or SHA-512 for JetBrains checksum sibling JetBrains publishes a single, generically-named ".checksum" sibling whose digest algorithm isn't disclosed by the filename. Older JBR 11 builds (e.g. jbrsdk_nomod-11_0_16-*-b2043.64.tar.gz) publish a SHA-256 digest there, while newer builds publish SHA-512. The JetBrains installer previously assumed SHA-512 unconditionally, so verification failed with "Malformed sha512 checksum metadata ... expected a 128-character hexadecimal digest" for those older builds, breaking the jetbrains 11 e2e job on macOS and Windows. fetchChecksum now accepts a list of candidate algorithms and infers the actual algorithm from the returned digest's length, preferring the strongest match. The JetBrains installer passes ['sha512', 'sha256']; all other callers are unaffected since they already pass a single, vendor-disclosed algorithm. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d * Use SapMachine archive checksum files Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a800a031-600e-4d28-b23e-be309555d38d
258 lines
7.9 KiB
TypeScript
258 lines
7.9 KiB
TypeScript
import * as core from '@actions/core';
|
|
import * as tc from '@actions/tool-cache';
|
|
import semver from 'semver';
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import {JavaBase} from '../base-installer.js';
|
|
import {
|
|
convertVersionToSemver,
|
|
extractJdkFile,
|
|
getDownloadArchiveExtension,
|
|
getGitHubHttpHeaders,
|
|
isVersionSatisfies,
|
|
renameWinArchive
|
|
} from '../../util.js';
|
|
import {IDragonwellVersions, IDragonwellAllVersions} from './models.js';
|
|
import {
|
|
JavaDownloadRelease,
|
|
JavaInstallerOptions,
|
|
JavaInstallerResults
|
|
} from '../base-models.js';
|
|
|
|
export class DragonwellDistribution extends JavaBase {
|
|
constructor(installerOptions: JavaInstallerOptions) {
|
|
super('Dragonwell', installerOptions);
|
|
}
|
|
|
|
protected async findPackageForDownload(
|
|
version: string
|
|
): Promise<JavaDownloadRelease> {
|
|
if (!this.stable) {
|
|
throw new Error('Early access versions are not supported by Dragonwell');
|
|
}
|
|
|
|
if (this.packageType !== 'jdk') {
|
|
throw new Error('Dragonwell provides only the `jdk` package type');
|
|
}
|
|
|
|
const availableVersions = await this.getAvailableVersions();
|
|
|
|
const matchedVersions = availableVersions
|
|
.filter(item => {
|
|
return isVersionSatisfies(version, item.jdk_version);
|
|
})
|
|
.map(item => {
|
|
return {
|
|
version: item.jdk_version,
|
|
url: item.download_link,
|
|
checksum: item.checksum
|
|
? {
|
|
algorithm: 'sha256',
|
|
value: item.checksum
|
|
}
|
|
: undefined
|
|
} as JavaDownloadRelease;
|
|
});
|
|
|
|
if (!matchedVersions.length) {
|
|
const availableVersionStrings = availableVersions.map(
|
|
item => item.jdk_version
|
|
);
|
|
throw this.createVersionNotFoundError(version, availableVersionStrings);
|
|
}
|
|
|
|
const resolvedVersion = matchedVersions[0];
|
|
return resolvedVersion;
|
|
}
|
|
|
|
private async getAvailableVersions(): Promise<IDragonwellVersions[]> {
|
|
const platform = this.getPlatformOption();
|
|
const arch = this.distributionArchitecture();
|
|
|
|
let fetchedDragonwellJson = await this.fetchJsonFromPrimaryUrl();
|
|
|
|
if (!fetchedDragonwellJson) {
|
|
fetchedDragonwellJson = await this.fetchJsonFromBackupUrl();
|
|
}
|
|
|
|
if (!fetchedDragonwellJson) {
|
|
throw new Error(
|
|
`Couldn't fetch Dragonwell versions information from both primary and backup urls`
|
|
);
|
|
}
|
|
|
|
core.debug(
|
|
'Successfully fetched information about available Dragonwell versions'
|
|
);
|
|
|
|
const availableVersions = this.parseVersions(
|
|
platform,
|
|
arch,
|
|
fetchedDragonwellJson
|
|
);
|
|
|
|
if (core.isDebug()) {
|
|
core.startGroup('Print information about available versions');
|
|
core.debug(availableVersions.map(item => item.jdk_version).join(', '));
|
|
core.endGroup();
|
|
}
|
|
|
|
return availableVersions;
|
|
}
|
|
|
|
protected async downloadTool(
|
|
javaRelease: JavaDownloadRelease
|
|
): Promise<JavaInstallerResults> {
|
|
core.info(
|
|
`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`
|
|
);
|
|
let javaArchivePath = await this.downloadAndVerify(javaRelease);
|
|
|
|
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 version = this.getToolcacheVersionName(javaRelease.version);
|
|
|
|
const javaPath = await tc.cacheDir(
|
|
archivePath,
|
|
this.toolcacheFolderName,
|
|
version,
|
|
this.architecture
|
|
);
|
|
|
|
return {version: javaRelease.version, path: javaPath};
|
|
}
|
|
|
|
private parseVersions(
|
|
platform: string,
|
|
arch: string,
|
|
dragonwellVersions: IDragonwellAllVersions
|
|
): IDragonwellVersions[] {
|
|
const eligibleVersions: IDragonwellVersions[] = [];
|
|
|
|
for (const majorVersion in dragonwellVersions) {
|
|
const majorVersionMap = dragonwellVersions[majorVersion];
|
|
for (let jdkVersion in majorVersionMap) {
|
|
const jdkVersionMap = majorVersionMap[jdkVersion];
|
|
if (!(platform in jdkVersionMap)) {
|
|
continue;
|
|
}
|
|
const platformMap = jdkVersionMap[platform];
|
|
if (!(arch in platformMap)) {
|
|
continue;
|
|
}
|
|
const archMap = platformMap[arch];
|
|
|
|
if (jdkVersion === 'latest') {
|
|
continue;
|
|
}
|
|
|
|
// Some version of Dragonwell JDK are numerated with help of non-semver notation (more then 3 digits).
|
|
// Common practice is to transform excess digits to the so-called semver build part, which is prefixed with the plus sign, to be able to operate with them using semver tools.
|
|
const jdkVersionNums: string[] = jdkVersion
|
|
.replace('+', '.')
|
|
.split('.');
|
|
jdkVersion = convertVersionToSemver(
|
|
`${jdkVersionNums.slice(0, 3).join('.')}.${
|
|
jdkVersionNums[jdkVersionNums.length - 1]
|
|
}`
|
|
);
|
|
|
|
for (const edition in archMap) {
|
|
eligibleVersions.push({
|
|
os: platform,
|
|
architecture: arch,
|
|
jdk_version: jdkVersion,
|
|
checksum: archMap[edition].sha256 ?? '',
|
|
download_link: archMap[edition].download_url,
|
|
edition: edition,
|
|
image_type: 'jdk'
|
|
});
|
|
break; // Get the first available link to the JDK. In most cases it should point to the Extended version of JDK, in rare cases like with v17 it points to the Standard version (the only available).
|
|
}
|
|
}
|
|
}
|
|
|
|
const sortedVersions = this.sortParsedVersions(eligibleVersions);
|
|
|
|
return sortedVersions;
|
|
}
|
|
|
|
// Sorts versions in descending order as by default data in JSON isn't sorted
|
|
private sortParsedVersions(
|
|
eligibleVersions: IDragonwellVersions[]
|
|
): IDragonwellVersions[] {
|
|
const sortedVersions = eligibleVersions.sort((versionObj1, versionObj2) => {
|
|
const version1 = versionObj1.jdk_version;
|
|
const version2 = versionObj2.jdk_version;
|
|
return semver.compareBuild(version1, version2);
|
|
});
|
|
return sortedVersions.reverse();
|
|
}
|
|
|
|
private getPlatformOption(): string {
|
|
switch (process.platform) {
|
|
case 'win32':
|
|
return 'windows';
|
|
default:
|
|
return process.platform;
|
|
}
|
|
}
|
|
|
|
private async fetchJsonFromPrimaryUrl(): Promise<IDragonwellAllVersions | null> {
|
|
const primaryUrl = 'https://dragonwell-jdk.io/map_with_checksum.json';
|
|
try {
|
|
core.debug(
|
|
`Trying to fetch available Dragonwell versions info from the primary url: ${primaryUrl}`
|
|
);
|
|
const fetchedDragonwellJson = (
|
|
await this.http.getJson<IDragonwellAllVersions>(primaryUrl)
|
|
).result;
|
|
return fetchedDragonwellJson;
|
|
} catch (err) {
|
|
core.debug(
|
|
`Fetching Dragonwell versions info from the primary link: ${primaryUrl} ended up with the error: ${
|
|
(err as Error).message
|
|
}`
|
|
);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private async fetchJsonFromBackupUrl(): Promise<IDragonwellAllVersions | null> {
|
|
const owner = 'dragonwell-releng';
|
|
const repository = 'dragonwell-setup-java';
|
|
const branch = 'main';
|
|
const filePath = 'releases.json';
|
|
|
|
const backupUrl = `https://api.github.com/repos/${owner}/${repository}/contents/${filePath}?ref=${branch}`;
|
|
|
|
const headers = getGitHubHttpHeaders();
|
|
|
|
try {
|
|
core.debug(
|
|
`Trying to fetch available Dragonwell versions info from the backup url: ${backupUrl}`
|
|
);
|
|
const fetchedDragonwellJson = (
|
|
await this.http.getJson<IDragonwellAllVersions>(backupUrl, headers)
|
|
).result;
|
|
return fetchedDragonwellJson;
|
|
} catch (err) {
|
|
core.debug(
|
|
`Fetching Dragonwell versions info from the backup url: ${backupUrl} ended up with the error: ${
|
|
(err as Error).message
|
|
}`
|
|
);
|
|
return null;
|
|
}
|
|
}
|
|
}
|