mirror of
https://github.com/actions/setup-java.git
synced 2026-08-18 03:32:51 +00:00
* Handle cache restore promise rejections Validate dependency cache providers before Java installation and settle cache restore failures immediately while preserving setup error precedence. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Make cache-rejection regression test deterministic Reject the cache restore only after it has started and while the Java installation is still pending, instead of relying on event-loop timing. Verified the test fails against the pre-fix implementation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Validate cache input after required input checks Move the package-manager validation out of the top of run() so that missing java-version/java-version-file and toolchain id errors keep their original precedence. Validation now happens immediately before the cache restore is started, which still fails fast before any JDK download. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com>
327 lines
9.4 KiB
TypeScript
327 lines
9.4 KiB
TypeScript
import fs from 'fs';
|
|
import * as core from '@actions/core';
|
|
import {
|
|
getBooleanInput,
|
|
getVersionFromFileContent,
|
|
isJdkCacheEnabled
|
|
} from './util.js';
|
|
import * as constants from './constants.js';
|
|
import * as path from 'path';
|
|
import {fileURLToPath} from 'url';
|
|
import {getJavaDistribution} from './distributions/distribution-factory.js';
|
|
import {JavaInstallerOptions} from './distributions/base-models.js';
|
|
import {configureProblemMatcher} from './problem-matcher.js';
|
|
import {validateToolchainIds} from './toolchain-ids.js';
|
|
|
|
export async function run() {
|
|
const versions = core.getMultilineInput(constants.INPUT_JAVA_VERSION);
|
|
let distributionName = core.getInput(constants.INPUT_DISTRIBUTION);
|
|
const versionFile = core.getInput(constants.INPUT_JAVA_VERSION_FILE);
|
|
const architecture = core.getInput(constants.INPUT_ARCHITECTURE);
|
|
const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE);
|
|
const jdkFile = getJdkFileInput();
|
|
const cache = core.getInput(constants.INPUT_CACHE);
|
|
const cacheJdk = isJdkCacheEnabled(cache);
|
|
const cacheDependencyPath = core.getInput(
|
|
constants.INPUT_CACHE_DEPENDENCY_PATH
|
|
);
|
|
const cachePath = core.getMultilineInput(constants.INPUT_CACHE_PATH);
|
|
const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false);
|
|
const forceDownload = getBooleanInput(constants.INPUT_FORCE_DOWNLOAD, false);
|
|
const setDefault = getBooleanInput(constants.INPUT_SET_DEFAULT, true);
|
|
const verifySignature = getBooleanInput(
|
|
constants.INPUT_VERIFY_SIGNATURE,
|
|
false
|
|
);
|
|
const verifySignaturePublicKey =
|
|
core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined;
|
|
const toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
|
|
let actionError: Error | undefined;
|
|
let cacheRestore: Promise<PromiseSettledResult<void>> | undefined;
|
|
const toolchainConfigurations: ToolchainConfiguration[] = [];
|
|
|
|
try {
|
|
core.startGroup('Installed distributions');
|
|
|
|
if (!versions.length && !versionFile) {
|
|
throw new Error('java-version or java-version-file input expected');
|
|
}
|
|
|
|
validateToolchainIds(versions, versionFile, toolchainIds);
|
|
|
|
if (!versions.length) {
|
|
core.debug(
|
|
'java-version input is empty, looking for java-version-file input'
|
|
);
|
|
const content = fs.readFileSync(versionFile).toString().trim();
|
|
|
|
const versionInfo = getVersionFromFileContent(
|
|
content,
|
|
distributionName,
|
|
versionFile
|
|
);
|
|
core.debug(`Parsed version from file '${versionInfo?.version}'`);
|
|
|
|
if (!versionInfo) {
|
|
throw new Error(
|
|
`No supported version was found in file ${versionFile}`
|
|
);
|
|
}
|
|
|
|
// Use distribution from file if available, otherwise use the input
|
|
if (versionInfo.distribution) {
|
|
core.info(
|
|
`Using distribution '${versionInfo.distribution}' from ${versionFile}`
|
|
);
|
|
distributionName = versionInfo.distribution;
|
|
} else if (!distributionName) {
|
|
throw new Error(
|
|
'distribution input is required when not specified in the version file'
|
|
);
|
|
}
|
|
|
|
const installerInputsOptions: installerInputsOptions = {
|
|
architecture,
|
|
packageType,
|
|
checkLatest,
|
|
forceDownload,
|
|
cacheJdk,
|
|
setDefault,
|
|
verifySignature,
|
|
verifySignaturePublicKey,
|
|
distributionName,
|
|
jdkFile,
|
|
toolchainIds
|
|
};
|
|
|
|
await validateCacheInput(cache);
|
|
cacheRestore = cache
|
|
? settle(startCacheRestore(cache, cacheDependencyPath, cachePath))
|
|
: undefined;
|
|
toolchainConfigurations.push(
|
|
await installVersion(versionInfo.version, installerInputsOptions)
|
|
);
|
|
} else {
|
|
// When using java-version input, distribution is still required
|
|
if (!distributionName) {
|
|
throw new Error('distribution input is required');
|
|
}
|
|
|
|
const installerInputsOptions: installerInputsOptions = {
|
|
architecture,
|
|
packageType,
|
|
checkLatest,
|
|
forceDownload,
|
|
cacheJdk,
|
|
setDefault,
|
|
verifySignature,
|
|
verifySignaturePublicKey,
|
|
distributionName,
|
|
jdkFile,
|
|
toolchainIds
|
|
};
|
|
|
|
await validateCacheInput(cache);
|
|
cacheRestore = cache
|
|
? settle(startCacheRestore(cache, cacheDependencyPath, cachePath))
|
|
: undefined;
|
|
for (const [index, version] of versions.entries()) {
|
|
toolchainConfigurations.push(
|
|
await installVersion(version, installerInputsOptions, index)
|
|
);
|
|
}
|
|
}
|
|
core.endGroup();
|
|
const matchersPath = path.join(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
'..',
|
|
'..',
|
|
'.github'
|
|
);
|
|
configureProblemMatcher(path.join(matchersPath, 'java.json'));
|
|
|
|
await configureMaven(toolchainConfigurations);
|
|
} catch (error) {
|
|
actionError = error as Error;
|
|
}
|
|
|
|
if (cacheRestore) {
|
|
const cacheResult = await cacheRestore;
|
|
if (cacheResult.status === 'rejected' && !actionError) {
|
|
actionError = cacheResult.reason as Error;
|
|
}
|
|
}
|
|
|
|
if (actionError) {
|
|
core.setFailed(actionError.message);
|
|
}
|
|
}
|
|
|
|
async function validateCacheInput(cache: string): Promise<void> {
|
|
if (!cache) {
|
|
return;
|
|
}
|
|
const {validatePackageManager} = await import('./cache.js');
|
|
validatePackageManager(cache);
|
|
}
|
|
|
|
function settle<T>(promise: Promise<T>): Promise<PromiseSettledResult<T>> {
|
|
return promise.then<PromiseFulfilledResult<T>, PromiseRejectedResult>(
|
|
value => ({status: 'fulfilled', value}),
|
|
reason => ({status: 'rejected', reason})
|
|
);
|
|
}
|
|
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
run();
|
|
} else {
|
|
// https://nodejs.org/api/modules.html#modules_accessing_the_main_module
|
|
core.info('the script is loaded as a module, so skipping the execution');
|
|
}
|
|
|
|
function getJdkFileInput(): string {
|
|
const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
|
|
const deprecatedJdkFile = core.getInput(constants.INPUT_JDK_FILE_DEPRECATED);
|
|
|
|
if (deprecatedJdkFile) {
|
|
core.warning(
|
|
`The '${constants.INPUT_JDK_FILE_DEPRECATED}' input is deprecated and may be removed in a future release. Please use '${constants.INPUT_JDK_FILE}' instead.`
|
|
);
|
|
}
|
|
|
|
return jdkFile || deprecatedJdkFile;
|
|
}
|
|
|
|
async function installVersion(
|
|
version: string,
|
|
options: installerInputsOptions,
|
|
toolchainId = 0
|
|
): Promise<ToolchainConfiguration> {
|
|
const {
|
|
distributionName,
|
|
jdkFile,
|
|
architecture,
|
|
packageType,
|
|
checkLatest,
|
|
forceDownload,
|
|
cacheJdk,
|
|
setDefault,
|
|
verifySignature,
|
|
verifySignaturePublicKey,
|
|
toolchainIds
|
|
} = options;
|
|
|
|
const installerOptions: JavaInstallerOptions = {
|
|
architecture,
|
|
packageType,
|
|
checkLatest,
|
|
forceDownload,
|
|
cacheJdk,
|
|
setDefault,
|
|
verifySignature,
|
|
verifySignaturePublicKey,
|
|
version
|
|
};
|
|
|
|
const distribution = await getJavaDistribution(
|
|
distributionName,
|
|
installerOptions,
|
|
jdkFile
|
|
);
|
|
if (!distribution) {
|
|
throw new Error(
|
|
`No supported distribution was found for input ${distributionName}`
|
|
);
|
|
}
|
|
|
|
const result = await distribution.setupJava();
|
|
|
|
// When the `latest` alias is used, the literal input isn't a real version, so
|
|
// pass the resolved version to the toolchains configuration instead.
|
|
const isLatest = version.trim().toLowerCase() === 'latest';
|
|
const toolchainVersion = isLatest ? result.version : version;
|
|
|
|
core.info('');
|
|
core.info('Java configuration:');
|
|
core.info(` Distribution: ${distributionName}`);
|
|
core.info(` Version: ${result.version}`);
|
|
core.info(` Path: ${result.path}`);
|
|
core.info('');
|
|
|
|
return {
|
|
version: toolchainVersion,
|
|
distributionName,
|
|
path: result.path,
|
|
toolchainId: toolchainIds[toolchainId]
|
|
};
|
|
}
|
|
|
|
interface installerInputsOptions {
|
|
architecture: string;
|
|
packageType: string;
|
|
checkLatest: boolean;
|
|
forceDownload: boolean;
|
|
cacheJdk: boolean;
|
|
setDefault: boolean;
|
|
verifySignature: boolean;
|
|
verifySignaturePublicKey: string | undefined;
|
|
distributionName: string;
|
|
jdkFile: string;
|
|
toolchainIds: Array<string>;
|
|
}
|
|
|
|
interface ToolchainConfiguration {
|
|
version: string;
|
|
distributionName: string;
|
|
path: string;
|
|
toolchainId?: string;
|
|
}
|
|
|
|
async function configureMaven(
|
|
toolchainConfigurations: ToolchainConfiguration[]
|
|
): Promise<void> {
|
|
const authentication = import('./auth.js').then(auth =>
|
|
auth.configureAuthentication()
|
|
);
|
|
const toolchains = configureInstalledToolchains(toolchainConfigurations);
|
|
|
|
const results = await Promise.allSettled([authentication, toolchains]);
|
|
const failure = results.find(
|
|
(result): result is PromiseRejectedResult => result.status === 'rejected'
|
|
);
|
|
if (failure) {
|
|
throw failure.reason;
|
|
}
|
|
|
|
const {configureMavenArgs} = await import('./maven-args.js');
|
|
configureMavenArgs();
|
|
}
|
|
|
|
async function configureInstalledToolchains(
|
|
toolchainConfigurations: ToolchainConfiguration[]
|
|
): Promise<void> {
|
|
const toolchains = await import('./toolchains.js');
|
|
for (const configuration of toolchainConfigurations) {
|
|
await toolchains.configureToolchains(
|
|
configuration.version,
|
|
configuration.distributionName,
|
|
configuration.path,
|
|
configuration.toolchainId
|
|
);
|
|
}
|
|
}
|
|
|
|
async function startCacheRestore(
|
|
cache: string,
|
|
cacheDependencyPath: string,
|
|
cachePath: string[]
|
|
): Promise<void> {
|
|
const {isCacheFeatureAvailable} = await import('./cache-feature.js');
|
|
if (!isCacheFeatureAvailable()) {
|
|
return;
|
|
}
|
|
|
|
const {restore} = await import('./cache.js');
|
|
await restore(cache, cacheDependencyPath, cachePath);
|
|
}
|