Files
setup-java/src/setup-java.ts
T
Bruno Borges 174d4b5609 Support pinning java-version as "latest" (#1093)
* Support pinning java-version as "latest"

Add a `latest` alias for the `java-version` input that floats to the newest
available stable (GA) release. It is normalized to the SemVer wildcard at the
base-installer layer and always resolves from remote (like `check-latest: true`).

List-based distributions resolve it automatically via the existing newest-first
matching. Corretto selects its newest available major; Oracle and GraalVM look up
the newest GA major via the Adoptium API and request it, failing with an actionable
error if that major isn't published yet. The jdkfile distribution rejects `latest`.

Closes #832

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Clarify latest note for oracle/graalvm version resolution vs download

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* graalvm-community: float latest to its own newest GA release

GraalVM Community publishes its releases on GitHub, so the `latest`
alias now matches against that real release list using the SemVer
wildcard instead of asking the Adoptium API for the newest GA major.
This prevents `latest` from hard-failing when GraalVM lags behind a
freshly released Java major (e.g. Adoptium reports 26 before GraalVM
ships it). Oracle GraalVM has no listing endpoint, so it keeps deriving
the newest major from Adoptium and errors clearly if that major is not
yet published.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Give latest+qualifier inputs (e.g. latest-ea) a targeted error

Inputs like 'latest-ea' had their '-ea' suffix stripped and fell through
to the generic SemVer validation, failing with a confusing "'latest' is
not valid SemVer" message even though 'latest' is supported. Add an
explicit guard so any 'latest*' value other than exactly 'latest' throws
a targeted error explaining that 'latest' resolves GA releases only and
cannot be combined with '-ea' or other qualifiers.

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: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-09 20:11:03 -04:00

221 lines
6.3 KiB
TypeScript

import fs from 'fs';
import * as core from '@actions/core';
import * as auth from './auth.js';
import {
getBooleanInput,
isCacheFeatureAvailable,
getVersionFromFileContent
} from './util.js';
import * as toolchains from './toolchains.js';
import * as constants from './constants.js';
import {restore} from './cache.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 {configureMavenArgs} from './maven-args.js';
async function run() {
try {
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 cacheDependencyPath = core.getInput(
constants.INPUT_CACHE_DEPENDENCY_PATH
);
const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, 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;
let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
core.startGroup('Installed distributions');
if (versions.length !== toolchainIds.length) {
toolchainIds = [];
}
if (!versions.length && !versionFile) {
throw new Error('java-version or java-version-file input expected');
}
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,
setDefault,
verifySignature,
verifySignaturePublicKey,
distributionName,
jdkFile,
toolchainIds
};
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,
setDefault,
verifySignature,
verifySignaturePublicKey,
distributionName,
jdkFile,
toolchainIds
};
for (const [index, version] of versions.entries()) {
await installVersion(version, installerInputsOptions, index);
}
}
core.endGroup();
const matchersPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'..',
'.github'
);
core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`);
await auth.configureAuthentication();
configureMavenArgs();
if (cache && isCacheFeatureAvailable()) {
await restore(cache, cacheDependencyPath);
}
} catch (error) {
core.setFailed((error as Error).message);
}
}
run();
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
) {
const {
distributionName,
jdkFile,
architecture,
packageType,
checkLatest,
setDefault,
verifySignature,
verifySignaturePublicKey,
toolchainIds
} = options;
const installerOptions: JavaInstallerOptions = {
architecture,
packageType,
checkLatest,
setDefault,
verifySignature,
verifySignaturePublicKey,
version
};
const distribution = 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;
await toolchains.configureToolchains(
toolchainVersion,
distributionName,
result.path,
toolchainIds[toolchainId]
);
core.info('');
core.info('Java configuration:');
core.info(` Distribution: ${distributionName}`);
core.info(` Version: ${result.version}`);
core.info(` Path: ${result.path}`);
core.info('');
}
interface installerInputsOptions {
architecture: string;
packageType: string;
checkLatest: boolean;
setDefault: boolean;
verifySignature: boolean;
verifySignaturePublicKey: string | undefined;
distributionName: string;
jdkFile: string;
toolchainIds: Array<string>;
}