From bf1fdd3036dca027a9f0fde608cdfd4580d4ab23 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:15:47 +0000 Subject: [PATCH] Infer distribution from .java-version and .tool-versions files --- .github/workflows/e2e-versions.yml | 30 +++++++++++++ README.md | 2 +- __tests__/util.test.ts | 70 ++++++++++++++++++++++++++++++ action.yml | 2 +- dist/cleanup/index.js | 62 +++++++++++++++++++++++--- dist/setup/index.js | 62 +++++++++++++++++++++++--- docs/advanced-usage.md | 18 +++++++- src/constants.ts | 21 +++++++++ src/util.ts | 55 +++++++++++++++++++++-- 9 files changed, 305 insertions(+), 17 deletions(-) diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 3080fd70..7592aa3a 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -670,6 +670,36 @@ jobs: run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash + setup-java-version-from-file-with-inferred-dist: + name: distribution inferred from file '${{ matrix.java-version-file }}' - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + java-version-file: ['.java-version', '.tool-versions'] + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Create .java-version file + shell: bash + run: echo "temurin-17.0.10" > .java-version + - name: Create .tool-versions file + shell: bash + run: echo "java temurin-17.0.10" > .tool-versions + - name: setup-java + uses: ./ + id: setup-java + with: + java-version-file: ${{matrix.java-version-file }} + - name: Verify Java + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" + shell: bash + setup-java-set-default: name: set-default option - ${{ matrix.os }} needs: setup-java-major-versions diff --git a/README.md b/README.md index fead4a69..5d74aacf 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ For more details, see the full release notes on the [releases page](https://git - `java-version-file`: The path to a file containing java version. Supported file types are `.java-version`, `.tool-versions`, and `.sdkmanrc`. See more details in [about .java-version-file](docs/advanced-usage.md#Java-version-file). - - `distribution`: Java [distribution](#supported-distributions). Required unless `java-version-file` points to `.sdkmanrc` with a recognized distribution suffix (for example `java=21.0.5-tem`). + - `distribution`: Java [distribution](#supported-distributions). Required unless `java-version-file` specifies a recognized distribution, for example a `.sdkmanrc` distribution suffix (`java=21.0.5-tem`) or a `.java-version`/`.tool-versions` distribution prefix (`temurin-21.0.5`). - `java-package`: The packaging variant of the chosen distribution. Possible values: `jdk`, `jre`, `jdk+fx`, `jre+fx`. Default value: `jdk`. diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 6782e687..6e5e006e 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -243,6 +243,76 @@ describe('getVersionFromFileContent', () => { ); }); }); + + describe('.java-version', () => { + it.each([ + ['temurin-21.0.5', '21.0.5', 'temurin'], + ['temurin-21.0.5+11.0.LTS', '21.0.5+11.0.LTS', 'temurin'], + ['zulu64-8.0.202', '8.0.202', 'zulu'], + ['temurin64-17', '17', 'temurin'], + ['corretto-17', '17', 'corretto'], // corretto reduced to major version + ['graalvm-community-17.0.9', '17.0.9', 'graalvm-community'] + ])( + 'parsing %s should return version %s and distribution %s', + (content: string, expectedVersion: string, expectedDist: string) => { + const actual = getVersionFromFileContent(content, '', '.java-version'); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBe(expectedDist); + } + ); + + it.each([ + ['17.0.7', '17.0.7'], + ['17', '17'], + ['15-ea', '15-ea'], + ['15.0.0-ea', '15.0.0-ea'], + ['8.0.282+8', '8.0.282+8'], + ['1.8.0.202', '8.0.202'], + ['openjdk64-11.0.2', '11.0.2'] // generic openjdk prefix is not a supported distribution + ])( + 'parsing %s should return version %s and undefined distribution', + (content: string, expectedVersion: string) => { + const actual = getVersionFromFileContent( + content, + 'temurin', + '.java-version' + ); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBeUndefined(); + } + ); + }); + + describe('.tool-versions', () => { + it.each([ + ['java temurin-21.0.5+11.0.LTS', '21.0.5+11.0.LTS', 'temurin'], + ['java corretto-11', '11', 'corretto'] + ])( + 'parsing %s should return version %s and distribution %s', + (content: string, expectedVersion: string, expectedDist: string) => { + const actual = getVersionFromFileContent(content, '', '.tool-versions'); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBe(expectedDist); + } + ); + + it.each([ + ['java 17', '17'], + ['java 17.0.10', '17.0.10'], + ['java openjdk64-17.0.10', '17.0.10'] // generic openjdk prefix is ignored + ])( + 'parsing %s should return version %s and undefined distribution', + (content: string, expectedVersion: string) => { + const actual = getVersionFromFileContent( + content, + 'temurin', + '.tool-versions' + ); + expect(actual?.version).toBe(expectedVersion); + expect(actual?.distribution).toBeUndefined(); + } + ); + }); }); describe('isGhes', () => { diff --git a/action.yml b/action.yml index e42fb05c..1dbe74b5 100644 --- a/action.yml +++ b/action.yml @@ -10,7 +10,7 @@ inputs: description: 'The path to a file containing the Java version to set up (.java-version, .tool-versions, .sdkmanrc). Used when java-version is not set. See examples of supported syntax in README file' required: false distribution: - description: 'Java distribution. See the list of supported distributions in README file. This input is required except when java-version-file points to .sdkmanrc with a recognized distribution suffix (e.g., java=21.0.5-tem).' + description: 'Java distribution. See the list of supported distributions in README file. This input is required except when java-version-file specifies a recognized distribution (e.g., .sdkmanrc suffix java=21.0.5-tem, or .java-version/.tool-versions prefix temurin-21.0.5).' required: false java-package: description: 'The package type (jdk, jre, jdk+fx, jre+fx)' diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 7b073345..7deaa9fb 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52241,7 +52241,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.SUPPORTED_DISTRIBUTIONS = exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -52276,6 +52276,26 @@ exports.MAVEN_ARGS_ENV = 'MAVEN_ARGS'; exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; +// Distribution names supported by the `distribution` input. Used to validate +// distribution identifiers inferred from a `.java-version`/`.tool-versions` file. +exports.SUPPORTED_DISTRIBUTIONS = [ + 'adopt', + 'adopt-hotspot', + 'adopt-openj9', + 'temurin', + 'zulu', + 'liberica', + 'microsoft', + 'semeru', + 'corretto', + 'oracle', + 'dragonwell', + 'sapmachine', + 'graalvm', + 'graalvm-community', + 'jetbrains', + 'kona' +]; /***/ }), @@ -52572,7 +52592,7 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c; + var _a, _b, _c, _d; let javaVersionRegExp; let extractedDistribution; function getFileName(versionFile) { @@ -52581,7 +52601,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*?)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier @@ -52589,7 +52609,10 @@ function getVersionFromFileContent(content, distributionName, versionFile) { /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { - javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; + // .java-version (jenv), version optionally prefixed with a distribution + // identifier, e.g. `temurin-21.0.5` or `openjdk64-11.0.2`. + javaVersionRegExp = + /(?:(?[a-zA-Z][a-zA-Z0-9-]*?)-)?(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } const match = content.match(javaVersionRegExp); const capturedVersion = ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.version) @@ -52601,6 +52624,16 @@ function getVersionFromFileContent(content, distributionName, versionFile) { extractedDistribution = mapSdkmanDistribution(sdkmanDist); core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); } + // Extract distribution from .java-version (jenv) or .tool-versions (asdf) file + if (versionFileName != '.sdkmanrc' && + ((_c = match === null || match === void 0 ? void 0 : match.groups) === null || _c === void 0 ? void 0 : _c.distribution) && + capturedVersion) { + const fileDistribution = match.groups.distribution; + extractedDistribution = mapJavaVersionFileDistribution(fileDistribution); + if (extractedDistribution) { + core.debug(`Parsed distribution '${extractedDistribution}' from identifier '${fileDistribution}' in '${versionFileName}'`); + } + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -52617,7 +52650,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution // (either explicitly provided or extracted from the version file) is in the list. if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { - const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; + const coerceVersion = (_d = semver.coerce(version)) !== null && _d !== void 0 ? _d : version; version = semver.major(coerceVersion).toString(); } return { @@ -52650,6 +52683,25 @@ function mapSdkmanDistribution(sdkmanDist) { } return mapped; } +// Map a distribution identifier found in a `.java-version` (jenv) or +// `.tool-versions` (asdf) file to a setup-java distribution name. +// jenv-style identifiers may carry an architecture suffix (e.g. `openjdk64`, +// `temurin64`), which is stripped before matching. Identifiers that do not map +// to a supported distribution (e.g. the generic `openjdk`) are ignored so the +// `distribution` input is used instead. +function mapJavaVersionFileDistribution(identifier) { + const normalized = identifier.toLowerCase(); + if (constants_1.SUPPORTED_DISTRIBUTIONS.includes(normalized)) { + return normalized; + } + // Strip a trailing architecture suffix (e.g. `temurin64` -> `temurin`). + const withoutArch = normalized.replace(/(32|64)$/, ''); + if (constants_1.SUPPORTED_DISTRIBUTIONS.includes(withoutArch)) { + return withoutArch; + } + core.debug(`Distribution identifier '${identifier}' from version file is not a supported distribution; falling back to the 'distribution' input.`); + return undefined; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; diff --git a/dist/setup/index.js b/dist/setup/index.js index 871b71de..43340678 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78001,7 +78001,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.SUPPORTED_DISTRIBUTIONS = exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = exports.MAVEN_ARGS_ENV = exports.INPUT_SHOW_DOWNLOAD_PROGRESS = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = exports.INPUT_VERIFY_SIGNATURE = exports.INPUT_SET_DEFAULT = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -78036,6 +78036,26 @@ exports.MAVEN_ARGS_ENV = 'MAVEN_ARGS'; exports.MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; exports.MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; +// Distribution names supported by the `distribution` input. Used to validate +// distribution identifiers inferred from a `.java-version`/`.tool-versions` file. +exports.SUPPORTED_DISTRIBUTIONS = [ + 'adopt', + 'adopt-hotspot', + 'adopt-openj9', + 'temurin', + 'zulu', + 'liberica', + 'microsoft', + 'semeru', + 'corretto', + 'oracle', + 'dragonwell', + 'sapmachine', + 'graalvm', + 'graalvm-community', + 'jetbrains', + 'kona' +]; /***/ }), @@ -81990,7 +82010,7 @@ function isCacheFeatureAvailable() { } exports.isCacheFeatureAvailable = isCacheFeatureAvailable; function getVersionFromFileContent(content, distributionName, versionFile) { - var _a, _b, _c; + var _a, _b, _c, _d; let javaVersionRegExp; let extractedDistribution; function getFileName(versionFile) { @@ -81999,7 +82019,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*?)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier @@ -82007,7 +82027,10 @@ function getVersionFromFileContent(content, distributionName, versionFile) { /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { - javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; + // .java-version (jenv), version optionally prefixed with a distribution + // identifier, e.g. `temurin-21.0.5` or `openjdk64-11.0.2`. + javaVersionRegExp = + /(?:(?[a-zA-Z][a-zA-Z0-9-]*?)-)?(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } const match = content.match(javaVersionRegExp); const capturedVersion = ((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.version) @@ -82019,6 +82042,16 @@ function getVersionFromFileContent(content, distributionName, versionFile) { extractedDistribution = mapSdkmanDistribution(sdkmanDist); core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); } + // Extract distribution from .java-version (jenv) or .tool-versions (asdf) file + if (versionFileName != '.sdkmanrc' && + ((_c = match === null || match === void 0 ? void 0 : match.groups) === null || _c === void 0 ? void 0 : _c.distribution) && + capturedVersion) { + const fileDistribution = match.groups.distribution; + extractedDistribution = mapJavaVersionFileDistribution(fileDistribution); + if (extractedDistribution) { + core.debug(`Parsed distribution '${extractedDistribution}' from identifier '${fileDistribution}' in '${versionFileName}'`); + } + } core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); if (!capturedVersion) { return null; @@ -82035,7 +82068,7 @@ function getVersionFromFileContent(content, distributionName, versionFile) { // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution // (either explicitly provided or extracted from the version file) is in the list. if (constants_1.DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { - const coerceVersion = (_c = semver.coerce(version)) !== null && _c !== void 0 ? _c : version; + const coerceVersion = (_d = semver.coerce(version)) !== null && _d !== void 0 ? _d : version; version = semver.major(coerceVersion).toString(); } return { @@ -82068,6 +82101,25 @@ function mapSdkmanDistribution(sdkmanDist) { } return mapped; } +// Map a distribution identifier found in a `.java-version` (jenv) or +// `.tool-versions` (asdf) file to a setup-java distribution name. +// jenv-style identifiers may carry an architecture suffix (e.g. `openjdk64`, +// `temurin64`), which is stripped before matching. Identifiers that do not map +// to a supported distribution (e.g. the generic `openjdk`) are ignored so the +// `distribution` input is used instead. +function mapJavaVersionFileDistribution(identifier) { + const normalized = identifier.toLowerCase(); + if (constants_1.SUPPORTED_DISTRIBUTIONS.includes(normalized)) { + return normalized; + } + // Strip a trailing architecture suffix (e.g. `temurin64` -> `temurin`). + const withoutArch = normalized.replace(/(32|64)$/, ''); + if (constants_1.SUPPORTED_DISTRIBUTIONS.includes(withoutArch)) { + return withoutArch; + } + core.debug(`Distribution identifier '${identifier}' from version file is not a supported distribution; falling back to the 'distribution' input.`); + return undefined; +} // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content) { return content.startsWith('1.') ? content.substring(2) : content; diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 18fa4e62..806d1f5a 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -779,8 +779,8 @@ steps: If the `java-version-file` input is specified, the action will extract the version from the file and install it. Supported files are `.java-version`, `.tool-versions` and `.sdkmanrc`. - * In `.java-version` file, only the version should be specified (e.g., 17.0.7). The `.java-version` file recognizes all variants of the version description according to [jenv](https://github.com/jenv/jenv). - * In `.tool-versions` file, java version should be preceded by the java keyword (e.g., java 17.0.7). The `.tool-versions` file supports version specifications in accordance with [asdf](https://github.com/asdf-vm/asdf) standards, adhering to Semantic Versioning ([semver](https://semver.org/)). + * In `.java-version` file, only the version should be specified (e.g., 17.0.7). The `.java-version` file recognizes all variants of the version description according to [jenv](https://github.com/jenv/jenv). A [supported distribution](#supported-distributions) may optionally be specified as a prefix (e.g., `temurin-17.0.7`), in which case setup-java infers the `distribution` input automatically. Generic prefixes that are not a supported distribution (e.g., `openjdk64-17.0.7`) are ignored and require setting `distribution` explicitly. + * In `.tool-versions` file, java version should be preceded by the java keyword (e.g., java 17.0.7). The `.tool-versions` file supports version specifications in accordance with [asdf](https://github.com/asdf-vm/asdf) standards, adhering to Semantic Versioning ([semver](https://semver.org/)). As with `.java-version`, a supported distribution may be specified as a prefix (e.g., `java temurin-17.0.7`) to infer the `distribution` input automatically. * In `.sdkmanrc` file, java version should be preceded by the `java=` prefix (e.g., `java=17.0.7-tem`). When a recognized SDKMAN distribution suffix is present, setup-java can infer the `distribution` input automatically. Unrecognized suffixes require setting `distribution` explicitly. The `.sdkmanrc` file supports version specifications in accordance with [file format](https://sdkman.io/usage#env-command), see [Sdkman! documentation](https://sdkman.io/jdks) for more information. Supported SDKMAN suffix mappings: @@ -816,12 +816,26 @@ steps: java=17.0.7-tem ``` +**Example step using `.java-version`** (distribution inferred from the file): +```yml + - name: Setup java + uses: actions/setup-java@v5 + with: + java-version-file: '.java-version' +``` + +**Example `.java-version`** (distribution inferred from the `temurin-` prefix): +``` +temurin-17.0.7 +``` + Valid entry options (does not apply to `.sdkmanrc`): ``` major versions: 8, 11, 16, 17, 21 more specific versions: 8.0.282+8, 8.0.232, 11.0, 11.0.4, 17.0 early access (EA) versions: 15-ea, 15.0.0-ea versions with specified distribution: openjdk64-11.0.2 +versions with inferred distribution: temurin-17.0.7, corretto-21 LTS versions : temurin-21.0.5+11.0.LTS ``` If the file contains multiple versions, only the first one will be recognized. diff --git a/src/constants.ts b/src/constants.ts index 67a5d7ea..8bc72b6b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -38,3 +38,24 @@ export const MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; export const MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; export const DISTRIBUTIONS_ONLY_MAJOR_VERSION = ['corretto']; + +// Distribution names supported by the `distribution` input. Used to validate +// distribution identifiers inferred from a `.java-version`/`.tool-versions` file. +export const SUPPORTED_DISTRIBUTIONS = [ + 'adopt', + 'adopt-hotspot', + 'adopt-openj9', + 'temurin', + 'zulu', + 'liberica', + 'microsoft', + 'semeru', + 'corretto', + 'oracle', + 'dragonwell', + 'sapmachine', + 'graalvm', + 'graalvm-community', + 'jetbrains', + 'kona' +]; diff --git a/src/util.ts b/src/util.ts index 32cb6bc1..e9f232db 100644 --- a/src/util.ts +++ b/src/util.ts @@ -6,7 +6,11 @@ import * as cache from '@actions/cache'; import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; -import {INPUT_JOB_STATUS, DISTRIBUTIONS_ONLY_MAJOR_VERSION} from './constants'; +import { + INPUT_JOB_STATUS, + DISTRIBUTIONS_ONLY_MAJOR_VERSION, + SUPPORTED_DISTRIBUTIONS +} from './constants'; import {OutgoingHttpHeaders} from 'http'; export function getTempDir() { @@ -147,13 +151,16 @@ export function getVersionFromFileContent( const versionFileName = getFileName(versionFile); if (versionFileName == '.tool-versions') { javaVersionRegExp = - /^java\s+(?:\S*-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + /^java\s+(?:(?\S*?)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; } else if (versionFileName == '.sdkmanrc') { // Match both version and optional distribution identifier javaVersionRegExp = /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } else { - javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; + // .java-version (jenv), version optionally prefixed with a distribution + // identifier, e.g. `temurin-21.0.5` or `openjdk64-11.0.2`. + javaVersionRegExp = + /(?:(?[a-zA-Z][a-zA-Z0-9-]*?)-)?(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } const match = content.match(javaVersionRegExp); @@ -170,6 +177,21 @@ export function getVersionFromFileContent( ); } + // Extract distribution from .java-version (jenv) or .tool-versions (asdf) file + if ( + versionFileName != '.sdkmanrc' && + match?.groups?.distribution && + capturedVersion + ) { + const fileDistribution = match.groups.distribution; + extractedDistribution = mapJavaVersionFileDistribution(fileDistribution); + if (extractedDistribution) { + core.debug( + `Parsed distribution '${extractedDistribution}' from identifier '${fileDistribution}' in '${versionFileName}'` + ); + } + } + core.debug( `Parsed version '${capturedVersion}' from file '${versionFileName}'` ); @@ -235,6 +257,33 @@ function mapSdkmanDistribution(sdkmanDist: string): string | undefined { return mapped; } +// Map a distribution identifier found in a `.java-version` (jenv) or +// `.tool-versions` (asdf) file to a setup-java distribution name. +// jenv-style identifiers may carry an architecture suffix (e.g. `openjdk64`, +// `temurin64`), which is stripped before matching. Identifiers that do not map +// to a supported distribution (e.g. the generic `openjdk`) are ignored so the +// `distribution` input is used instead. +function mapJavaVersionFileDistribution( + identifier: string +): string | undefined { + const normalized = identifier.toLowerCase(); + + if (SUPPORTED_DISTRIBUTIONS.includes(normalized)) { + return normalized; + } + + // Strip a trailing architecture suffix (e.g. `temurin64` -> `temurin`). + const withoutArch = normalized.replace(/(32|64)$/, ''); + if (SUPPORTED_DISTRIBUTIONS.includes(withoutArch)) { + return withoutArch; + } + + core.debug( + `Distribution identifier '${identifier}' from version file is not a supported distribution; falling back to the 'distribution' input.` + ); + return undefined; +} + // By convention, action expects version 8 in the format `8.*` instead of `1.8` function avoidOldNotation(content: string): string { return content.startsWith('1.') ? content.substring(2) : content;