diff --git a/README.md b/README.md
index 904521da..7f774100 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ The `setup-java` action provides the following functionality for GitHub Actions
- Caching dependencies managed by Apache Maven
- Caching dependencies managed by Gradle
- Caching dependencies managed by sbt
+- [Maven Toolchains declaration](https://maven.apache.org/guides/mini/guide-using-toolchains.html) for specified JDK versions
This action allows you to work with Java and Scala projects.
@@ -37,7 +38,7 @@ This action allows you to work with Java and Scala projects.
- `cache`: Quick [setup caching](#caching-packages-dependencies) for the dependencies managed through one of the predifined package managers. It can be one of "maven", "gradle" or "sbt".
#### Maven options
- The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin. See [advanced usage](docs/advanced-usage.md) for more.
+ The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin as well as Apache Maven Toolchains. See [advanced usage](docs/advanced-usage.md) for more.
- `overwrite-settings`: By default action overwrites the settings.xml. In order to skip generation of file if it exists set this to `false`.
@@ -53,6 +54,10 @@ This action allows you to work with Java and Scala projects.
- `gpg-passphrase`: description: 'Environment variable name for the GPG private key passphrase. Default is GPG_PASSPHRASE.
+ - `mvn-toolchain-id`: Name of Maven Toolchain ID if the default name of `${distribution}_${java-version}` is not wanted.
+
+ - `mvn-toolchain-vendor`: Name of Maven Toolchain Vendor if the default name of `${distribution}` is not wanted.
+
### Basic Configuration
#### Eclipse Temurin
@@ -203,7 +208,11 @@ All versions are added to the PATH. The last version will be used and available
15
```
+### Using Maven Toolchains
+In the example above multiple JDKs are installed for the same job. The result after the last JDK is installed is a Maven Toolchains declaration containing references to all three JDKs. The values for `id`, `version`, and `vendor` of the individual Toolchain entries are the given input values for `distribution` and `java-version` (`vendor` being the combination of `${distribution}_${java-version}`) by default.
+
### Advanced Configuration
+
- [Selecting a Java distribution](docs/advanced-usage.md#Selecting-a-Java-distribution)
- [Eclipse Temurin](docs/advanced-usage.md#Eclipse-Temurin)
- [Adopt](docs/advanced-usage.md#Adopt)
@@ -219,6 +228,7 @@ All versions are added to the PATH. The last version will be used and available
- [Publishing using Apache Maven](docs/advanced-usage.md#Publishing-using-Apache-Maven)
- [Publishing using Gradle](docs/advanced-usage.md#Publishing-using-Gradle)
- [Hosted Tool Cache](docs/advanced-usage.md#Hosted-Tool-Cache)
+- [Modifying Maven Toolchains](docs/advanced-usage.md#Modifying-Maven-Toolchains)
## License
diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts
index 94ad7e1c..c5d2ffc3 100644
--- a/__tests__/auth.test.ts
+++ b/__tests__/auth.test.ts
@@ -5,9 +5,10 @@ import * as core from '@actions/core';
import os from 'os';
import * as auth from '../src/auth';
+import { M2_DIR, MVN_SETTINGS_FILE } from '../src/constants';
-const m2Dir = path.join(__dirname, auth.M2_DIR);
-const settingsFile = path.join(m2Dir, auth.SETTINGS_FILE);
+const m2Dir = path.join(__dirname, M2_DIR);
+const settingsFile = path.join(m2Dir, MVN_SETTINGS_FILE);
describe('auth tests', () => {
let spyOSHomedir: jest.SpyInstance;
@@ -38,7 +39,7 @@ describe('auth tests', () => {
const password = 'TOLKIEN';
const altHome = path.join(__dirname, 'runner', 'settings');
- const altSettingsFile = path.join(altHome, auth.SETTINGS_FILE);
+ const altSettingsFile = path.join(altHome, MVN_SETTINGS_FILE);
await io.rmRF(altHome); // ensure it doesn't already exist
await auth.createAuthenticationSettings(id, username, password, altHome, true);
diff --git a/__tests__/toolchains.test.ts b/__tests__/toolchains.test.ts
new file mode 100644
index 00000000..ff6fdab0
--- /dev/null
+++ b/__tests__/toolchains.test.ts
@@ -0,0 +1,292 @@
+import * as fs from 'fs';
+import os from 'os';
+import * as path from 'path';
+import * as core from '@actions/core';
+import * as io from '@actions/io';
+import * as toolchains from '../src/toolchains';
+import { M2_DIR, MVN_TOOLCHAINS_FILE } from '../src/constants';
+
+const m2Dir = path.join(__dirname, M2_DIR);
+const toolchainsFile = path.join(m2Dir, MVN_TOOLCHAINS_FILE);
+
+describe('toolchains tests', () => {
+ let spyOSHomedir: jest.SpyInstance;
+ let spyInfo: jest.SpyInstance;
+
+ beforeEach(async () => {
+ await io.rmRF(m2Dir);
+ spyOSHomedir = jest.spyOn(os, 'homedir');
+ spyOSHomedir.mockReturnValue(__dirname);
+ spyInfo = jest.spyOn(core, 'info');
+ spyInfo.mockImplementation(() => null);
+ }, 300000);
+
+ afterAll(async () => {
+ try {
+ await io.rmRF(m2Dir);
+ } catch {
+ console.log('Failed to remove test directories');
+ }
+ jest.resetAllMocks();
+ jest.clearAllMocks();
+ jest.restoreAllMocks();
+ }, 100000);
+
+ it('creates toolchains.xml in alternate locations', async () => {
+ const jdkInfo = {
+ version: '17',
+ vendor: 'Eclipse Temurin',
+ id: 'temurin_17',
+ jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+ };
+
+ const altHome = path.join(__dirname, 'runner', 'toolchains');
+ const altToolchainsFile = path.join(altHome, MVN_TOOLCHAINS_FILE);
+ await io.rmRF(altHome); // ensure it doesn't already exist
+
+ await toolchains.createToolchainsSettings({
+ jdkInfo,
+ settingsDirectory: altHome,
+ overwriteSettings: true
+ });
+
+ expect(fs.existsSync(m2Dir)).toBe(false);
+ expect(fs.existsSync(toolchainsFile)).toBe(false);
+
+ expect(fs.existsSync(altHome)).toBe(true);
+ expect(fs.existsSync(altToolchainsFile)).toBe(true);
+ expect(fs.readFileSync(altToolchainsFile, 'utf-8')).toEqual(
+ toolchains.generateToolchainDefinition(
+ '',
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ );
+
+ await io.rmRF(altHome);
+ }, 100000);
+
+ it('creates toolchains.xml with minimal configuration', async () => {
+ const jdkInfo = {
+ version: '17',
+ vendor: 'Eclipse Temurin',
+ id: 'temurin_17',
+ jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+ };
+
+ const result = `
+
+
+ jdk
+
+ 17
+ Eclipse Temurin
+ temurin_17
+
+
+ /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64
+
+
+`;
+
+ await toolchains.createToolchainsSettings({
+ jdkInfo,
+ settingsDirectory: m2Dir,
+ overwriteSettings: true
+ });
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+ expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+ toolchains.generateToolchainDefinition(
+ '',
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ );
+ expect(
+ toolchains.generateToolchainDefinition(
+ '',
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ ).toEqual(result);
+ }, 100000);
+
+ it('reuses existing toolchains.xml files', async () => {
+ const jdkInfo = {
+ version: '17',
+ vendor: 'Eclipse Temurin',
+ id: 'temurin_17',
+ jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+ };
+
+ const originalFile = `
+
+ jdk
+
+ 1.6
+ Sun
+ sun_1.6
+
+
+ /opt/jdk/sun/1.6
+
+
+ `;
+ const result = `
+
+
+ jdk
+
+ 1.6
+ Sun
+ sun_1.6
+
+
+ /opt/jdk/sun/1.6
+
+
+
+ jdk
+
+ 17
+ Eclipse Temurin
+ temurin_17
+
+
+ /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64
+
+
+`;
+
+ fs.mkdirSync(m2Dir, { recursive: true });
+ fs.writeFileSync(toolchainsFile, originalFile);
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+
+ await toolchains.createToolchainsSettings({
+ jdkInfo,
+ settingsDirectory: m2Dir,
+ overwriteSettings: true
+ });
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+ expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+ toolchains.generateToolchainDefinition(
+ originalFile,
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ );
+ expect(
+ toolchains.generateToolchainDefinition(
+ originalFile,
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ ).toEqual(result);
+ }, 100000);
+
+ it('does not overwrite existing toolchains.xml files', async () => {
+ const jdkInfo = {
+ version: '17',
+ vendor: 'Eclipse Temurin',
+ id: 'temurin_17',
+ jdkHome: '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64'
+ };
+
+ const originalFile = `
+
+ jdk
+
+ 1.6
+ Sun
+ sun_1.6
+
+
+ /opt/jdk/sun/1.6
+
+
+ `;
+
+ fs.mkdirSync(m2Dir, { recursive: true });
+ fs.writeFileSync(toolchainsFile, originalFile);
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+
+ await toolchains.createToolchainsSettings({
+ jdkInfo,
+ settingsDirectory: m2Dir,
+ overwriteSettings: false
+ });
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+ expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(originalFile);
+ }, 100000);
+
+ it('generates valid toolchains.xml with minimal configuration', () => {
+ const jdkInfo = {
+ version: 'JAVA_VERSION',
+ vendor: 'JAVA_VENDOR',
+ id: 'VENDOR_VERSION',
+ jdkHome: 'JAVA_HOME'
+ };
+
+ const expectedToolchains = `
+
+
+ jdk
+
+ ${jdkInfo.version}
+ ${jdkInfo.vendor}
+ ${jdkInfo.id}
+
+
+ ${jdkInfo.jdkHome}
+
+
+`;
+
+ expect(
+ toolchains.generateToolchainDefinition(
+ '',
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ )
+ ).toEqual(expectedToolchains);
+ }, 100000);
+
+ it('creates toolchains.xml with correct id when none is supplied', async () => {
+ const version = '17';
+ const distributionName = 'temurin';
+ const id = 'temurin_17';
+ const jdkHome = '/opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.1-12/x64';
+
+ await toolchains.configureToolchains(version, distributionName, jdkHome, undefined);
+
+ expect(fs.existsSync(m2Dir)).toBe(true);
+ expect(fs.existsSync(toolchainsFile)).toBe(true);
+ expect(fs.readFileSync(toolchainsFile, 'utf-8')).toEqual(
+ toolchains.generateToolchainDefinition('', version, distributionName, id, jdkHome)
+ );
+ }, 100000);
+});
diff --git a/action.yml b/action.yml
index 499eae3f..7bd7180f 100644
--- a/action.yml
+++ b/action.yml
@@ -62,6 +62,12 @@ inputs:
token:
description: Used to pull java versions from setup-java. Since there is a default value, token is typically not supplied by the user.
default: ${{ github.token }}
+ mvn-toolchain-id:
+ description: 'Name of Maven Toolchain ID if the default name of "${distribution}_${java-version}" is not wanted. See examples of supported syntax in Advanced Usage file'
+ required: false
+ mvn-toolchain-vendor:
+ description: 'Name of Maven Toolchain Vendor if the default name of "${distribution}" is not wanted. See examples of supported syntax in Advanced Usage file'
+ required: false
outputs:
distribution:
description: 'Distribution of Java that has been installed'
diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js
index e4fcbd59..df9ec30c 100644
--- a/dist/cleanup/index.js
+++ b/dist/cleanup/index.js
@@ -68406,7 +68406,7 @@ else {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = 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_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
+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 = 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_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = 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_ARCHITECTURE = 'architecture';
@@ -68426,6 +68426,11 @@ exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE';
exports.INPUT_CACHE = 'cache';
exports.INPUT_JOB_STATUS = 'job-status';
exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+exports.M2_DIR = '.m2';
+exports.MVN_SETTINGS_FILE = 'settings.xml';
+exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
/***/ }),
diff --git a/dist/setup/index.js b/dist/setup/index.js
index e19da558..0bdc9dcd 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -103216,7 +103216,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.generate = exports.createAuthenticationSettings = exports.configureAuthentication = exports.SETTINGS_FILE = exports.M2_DIR = void 0;
+exports.generate = exports.createAuthenticationSettings = exports.configureAuthentication = void 0;
const path = __importStar(__nccwpck_require__(1017));
const core = __importStar(__nccwpck_require__(2186));
const io = __importStar(__nccwpck_require__(7436));
@@ -103226,14 +103226,12 @@ const xmlbuilder2_1 = __nccwpck_require__(151);
const constants = __importStar(__nccwpck_require__(9042));
const gpg = __importStar(__nccwpck_require__(3759));
const util_1 = __nccwpck_require__(2629);
-exports.M2_DIR = '.m2';
-exports.SETTINGS_FILE = 'settings.xml';
function configureAuthentication() {
return __awaiter(this, void 0, void 0, function* () {
const id = core.getInput(constants.INPUT_SERVER_ID);
const username = core.getInput(constants.INPUT_SERVER_USERNAME);
const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
- const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), exports.M2_DIR);
+ const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
const overwriteSettings = util_1.getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
const gpgPrivateKey = core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
const gpgPassphrase = core.getInput(constants.INPUT_GPG_PASSPHRASE) ||
@@ -103252,7 +103250,7 @@ function configureAuthentication() {
exports.configureAuthentication = configureAuthentication;
function createAuthenticationSettings(id, username, password, settingsDirectory, overwriteSettings, gpgPassphrase = undefined) {
return __awaiter(this, void 0, void 0, function* () {
- core.info(`Creating ${exports.SETTINGS_FILE} with server-id: ${id}`);
+ core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`);
// when an alternate m2 location is specified use only that location (no .m2 directory)
// otherwise use the home/.m2/ path
yield io.mkdirP(settingsDirectory);
@@ -103294,7 +103292,7 @@ function generate(id, username, password, gpgPassphrase) {
exports.generate = generate;
function write(directory, settings, overwriteSettings) {
return __awaiter(this, void 0, void 0, function* () {
- const location = path.join(directory, exports.SETTINGS_FILE);
+ const location = path.join(directory, constants.MVN_SETTINGS_FILE);
const settingsExists = fs.existsSync(location);
if (settingsExists && overwriteSettings) {
core.info(`Overwriting existing file ${location}`);
@@ -103510,7 +103508,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
-exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = 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_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0;
+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 = 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_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = 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_ARCHITECTURE = 'architecture';
@@ -103530,6 +103528,11 @@ exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE';
exports.INPUT_CACHE = 'cache';
exports.INPUT_JOB_STATUS = 'job-status';
exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+exports.M2_DIR = '.m2';
+exports.MVN_SETTINGS_FILE = 'settings.xml';
+exports.MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+exports.INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+exports.INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
/***/ }),
@@ -104952,6 +104955,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
const core = __importStar(__nccwpck_require__(2186));
const auth = __importStar(__nccwpck_require__(3497));
const util_1 = __nccwpck_require__(2629);
+const toolchains = __importStar(__nccwpck_require__(9322));
const constants = __importStar(__nccwpck_require__(9042));
const cache_1 = __nccwpck_require__(4810);
const path = __importStar(__nccwpck_require__(1017));
@@ -104966,8 +104970,12 @@ function run() {
const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
const cache = core.getInput(constants.INPUT_CACHE);
const checkLatest = util_1.getBooleanInput(constants.INPUT_CHECK_LATEST, false);
+ let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
+ if (versions.length !== toolchainIds.length) {
+ toolchainIds = [];
+ }
core.startGroup('Installed distributions');
- for (const version of versions) {
+ for (const [index, version] of versions.entries()) {
const installerOptions = {
architecture,
packageType,
@@ -104979,6 +104987,7 @@ function run() {
throw new Error(`No supported distribution was found for input ${distributionName}`);
}
const result = yield distribution.setupJava();
+ yield toolchains.configureToolchains(version, distributionName, result.path, toolchainIds[index]);
core.info('');
core.info('Java configuration:');
core.info(` Distribution: ${distributionName}`);
@@ -105002,6 +105011,166 @@ function run() {
run();
+/***/ }),
+
+/***/ 9322:
+/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
+
+"use strict";
+
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || function (mod) {
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
+ __setModuleDefault(result, mod);
+ return result;
+};
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+};
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.generateToolchainDefinition = exports.createToolchainsSettings = exports.configureToolchains = void 0;
+const fs = __importStar(__nccwpck_require__(7147));
+const os = __importStar(__nccwpck_require__(2037));
+const path = __importStar(__nccwpck_require__(1017));
+const core = __importStar(__nccwpck_require__(2186));
+const io = __importStar(__nccwpck_require__(7436));
+const constants = __importStar(__nccwpck_require__(9042));
+const util_1 = __nccwpck_require__(2629);
+const xmlbuilder2_1 = __nccwpck_require__(151);
+function configureToolchains(version, distributionName, jdkHome, toolchainId) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const vendor = core.getInput(constants.INPUT_MVN_TOOLCHAIN_VENDOR) || distributionName;
+ const id = toolchainId || `${vendor}_${version}`;
+ const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
+ const overwriteSettings = util_1.getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
+ yield createToolchainsSettings({
+ jdkInfo: {
+ version,
+ vendor,
+ id,
+ jdkHome
+ },
+ settingsDirectory,
+ overwriteSettings
+ });
+ });
+}
+exports.configureToolchains = configureToolchains;
+function createToolchainsSettings({ jdkInfo, settingsDirectory, overwriteSettings }) {
+ return __awaiter(this, void 0, void 0, function* () {
+ core.info(`Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`);
+ // when an alternate m2 location is specified use only that location (no .m2 directory)
+ // otherwise use the home/.m2/ path
+ yield io.mkdirP(settingsDirectory);
+ const originalToolchains = yield readExistingToolchainsFile(settingsDirectory);
+ const updatedToolchains = generateToolchainDefinition(originalToolchains, jdkInfo.version, jdkInfo.vendor, jdkInfo.id, jdkInfo.jdkHome);
+ yield writeToolchainsFileToDisk(settingsDirectory, updatedToolchains, overwriteSettings);
+ });
+}
+exports.createToolchainsSettings = createToolchainsSettings;
+// only exported for testing purposes
+function generateToolchainDefinition(original, version, vendor, id, jdkHome) {
+ let xmlObj;
+ if (original === null || original === void 0 ? void 0 : original.length) {
+ xmlObj = xmlbuilder2_1.create(original)
+ .root()
+ .ele({
+ toolchain: {
+ type: 'jdk',
+ provides: {
+ version: `${version}`,
+ vendor: `${vendor}`,
+ id: `${id}`
+ },
+ configuration: {
+ jdkHome: `${jdkHome}`
+ }
+ }
+ });
+ }
+ else
+ xmlObj = xmlbuilder2_1.create({
+ toolchains: {
+ '@xmlns': 'https://maven.apache.org/TOOLCHAINS/1.1.0',
+ '@xmlns:xsi': 'https://www.w3.org/2001/XMLSchema-instance',
+ '@xsi:schemaLocation': 'https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
+ toolchain: [
+ {
+ type: 'jdk',
+ provides: {
+ version: `${version}`,
+ vendor: `${vendor}`,
+ id: `${id}`
+ },
+ configuration: {
+ jdkHome: `${jdkHome}`
+ }
+ }
+ ]
+ }
+ });
+ return xmlObj.end({
+ format: 'xml',
+ wellFormed: false,
+ headless: false,
+ prettyPrint: true,
+ width: 80
+ });
+}
+exports.generateToolchainDefinition = generateToolchainDefinition;
+function readExistingToolchainsFile(directory) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+ if (fs.existsSync(location)) {
+ return fs.readFileSync(location, {
+ encoding: 'utf-8',
+ flag: 'r'
+ });
+ }
+ return '';
+ });
+}
+function writeToolchainsFileToDisk(directory, settings, overwriteSettings) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+ const settingsExists = fs.existsSync(location);
+ if (settingsExists && overwriteSettings) {
+ core.info(`Overwriting existing file ${location}`);
+ }
+ else if (!settingsExists) {
+ core.info(`Writing to ${location}`);
+ }
+ else {
+ core.info(`Skipping generation of ${location} because file already exists and overwriting is not enabled`);
+ return;
+ }
+ return fs.writeFileSync(location, settings, {
+ encoding: 'utf-8',
+ flag: 'w'
+ });
+ });
+}
+
+
/***/ }),
/***/ 2629:
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index befe52df..93ebd854 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -14,6 +14,7 @@
- [Publishing using Apache Maven](#Publishing-using-Apache-Maven)
- [Publishing using Gradle](#Publishing-using-Gradle)
- [Hosted Tool Cache](#Hosted-Tool-Cache)
+- [Modifying Maven Toolchains](#Modifying-Maven-Toolchains)
See [action.yml](../action.yml) for more details on task inputs.
@@ -350,3 +351,100 @@ GitHub Hosted Runners have a tool cache that comes with some Java versions pre-i
Currently, LTS versions of Adopt OpenJDK (`adopt`) are cached on the GitHub Hosted Runners.
The tools cache gets updated on a weekly basis. For information regarding locally cached versions of Java on GitHub hosted runners, check out [GitHub Actions Virtual Environments](https://github.com/actions/virtual-environments).
+
+## Modifying Maven Toolchains
+The `setup-java` action generates a basic [Maven Toolchains declaration](https://maven.apache.org/guides/mini/guide-using-toolchains.html) for specified Java versions by either creating a minimal toolchains file or extending an existing declaration with the additional JDKs.
+
+### Installing Multiple JDKs With Toolchains
+Subsequent calls to `setup-java` with distinct distribution and version parameters will continue to extend the toolchains declaration and make all specified Java versions available.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+ with:
+ distribution: ''
+ java-version: |
+ 8
+ 11
+
+- uses: actions/setup-java@v3
+ with:
+ distribution: ''
+ java-version: 15
+```
+
+The result is a Toolchain with entries for JDKs 8, 11 and 15. You can even combine this with custom JDKs of arbitrary versions:
+
+```yaml
+- run: |
+ download_url="https://example.com/java/jdk/6u45-b06/jdk-6u45-linux-x64.tar.gz"
+ wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
+- uses: actions/setup-java@v3
+ with:
+ distribution: 'jdkfile'
+ jdkFile: ${{ runner.temp }}/java_package.tar.gz
+ java-version: '1.6'
+ architecture: x64
+```
+
+This will generate a Toolchains entry with the following values: `version: 1.6`, `vendor: jkdfile`, `id: Oracle_1.6`.
+
+### Modifying The Toolchain Vendor For JDKs
+Each JDK provider will receive a default `vendor` using the `distribution` input value but this can be overridden with the `mvn-toolchain-vendor` parameter as follows.
+
+```yaml
+- run: |
+ download_url="https://example.com/java/jdk/6u45-b06/jdk-6u45-linux-x64.tar.gz"
+ wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
+- uses: actions/setup-java@v3
+ with:
+ distribution: 'jdkfile'
+ jdkFile: ${{ runner.temp }}/java_package.tar.gz
+ java-version: '1.6'
+ architecture: x64
+ mvn-toolchain-vendor: 'Oracle'
+```
+
+This will generate a Toolchains entry with the following values: `version: 1.6`, `vendor: Oracle`, `id: Oracle_1.6`.
+
+In case you install multiple versions of Java at once with multi-line `java-version` input setting the `mvn-toolchain-vendor` still only accepts one value and will use this value for installed JDKs as expected when installing multiple versions of the same `distribution`.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+ with:
+ distribution: ''
+ java-version: |
+ 8
+ 11
+ mvn-toolchain-vendor: Eclipse Temurin
+```
+
+### Modifying The Toolchain ID For JDKs
+Each JDK provider will receive a default `id` based on the combination of `distribution` and `java-version` in the format of `distribution_java-version` (e.g. `temurin_11`) but this can be overridden with the `mvn-toolchain-id` parameter as follows.
+
+```yaml
+steps:
+- uses: actions/checkout@v3
+- uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: '11'
+ mvn-toolchain-id: 'some_other_id'
+- run: java -cp java HelloWorldApp
+```
+
+In case you install multiple versions of Java at once you can use the same syntax as used in `java-versions`. Please note that you have to declare an ID for all Java versions that will be installed or the `mvn-toolchain-id` instruction will be skipped wholesale due to mapping ambiguities.
+
+```yaml
+steps:
+- uses: actions/setup-java@v3
+ with:
+ distribution: ''
+ java-version: |
+ 8
+ 11
+ mvn-toolchain-id: |
+ something_else
+ something_other
+```
diff --git a/src/auth.ts b/src/auth.ts
index a52306b0..f697ddcd 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -10,15 +10,12 @@ import * as constants from './constants';
import * as gpg from './gpg';
import { getBooleanInput } from './util';
-export const M2_DIR = '.m2';
-export const SETTINGS_FILE = 'settings.xml';
-
export async function configureAuthentication() {
const id = core.getInput(constants.INPUT_SERVER_ID);
const username = core.getInput(constants.INPUT_SERVER_USERNAME);
const password = core.getInput(constants.INPUT_SERVER_PASSWORD);
const settingsDirectory =
- core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), M2_DIR);
+ core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
const overwriteSettings = getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
const gpgPrivateKey =
core.getInput(constants.INPUT_GPG_PRIVATE_KEY) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY;
@@ -54,7 +51,7 @@ export async function createAuthenticationSettings(
overwriteSettings: boolean,
gpgPassphrase: string | undefined = undefined
) {
- core.info(`Creating ${SETTINGS_FILE} with server-id: ${id}`);
+ core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`);
// when an alternate m2 location is specified use only that location (no .m2 directory)
// otherwise use the home/.m2/ path
await io.mkdirP(settingsDirectory);
@@ -106,7 +103,7 @@ export function generate(
}
async function write(directory: string, settings: string, overwriteSettings: boolean) {
- const location = path.join(directory, SETTINGS_FILE);
+ const location = path.join(directory, constants.MVN_SETTINGS_FILE);
const settingsExists = fs.existsSync(location);
if (settingsExists && overwriteSettings) {
core.info(`Overwriting existing file ${location}`);
diff --git a/src/constants.ts b/src/constants.ts
index 94d7667f..ad8709e7 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -20,3 +20,9 @@ export const INPUT_CACHE = 'cache';
export const INPUT_JOB_STATUS = 'job-status';
export const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint';
+
+export const M2_DIR = '.m2';
+export const MVN_SETTINGS_FILE = 'settings.xml';
+export const MVN_TOOLCHAINS_FILE = 'toolchains.xml';
+export const INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id';
+export const INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor';
diff --git a/src/setup-java.ts b/src/setup-java.ts
index 621883a8..048c5dd6 100644
--- a/src/setup-java.ts
+++ b/src/setup-java.ts
@@ -1,6 +1,7 @@
import * as core from '@actions/core';
import * as auth from './auth';
import { getBooleanInput, isCacheFeatureAvailable } from './util';
+import * as toolchains from './toolchains';
import * as constants from './constants';
import { restore } from './cache';
import * as path from 'path';
@@ -16,9 +17,14 @@ async function run() {
const jdkFile = core.getInput(constants.INPUT_JDK_FILE);
const cache = core.getInput(constants.INPUT_CACHE);
const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false);
+ let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID);
+
+ if (versions.length !== toolchainIds.length) {
+ toolchainIds = [];
+ }
core.startGroup('Installed distributions');
- for (const version of versions) {
+ for (const [index, version] of versions.entries()) {
const installerOptions: JavaInstallerOptions = {
architecture,
packageType,
@@ -32,6 +38,12 @@ async function run() {
}
const result = await distribution.setupJava();
+ await toolchains.configureToolchains(
+ version,
+ distributionName,
+ result.path,
+ toolchainIds[index]
+ );
core.info('');
core.info('Java configuration:');
diff --git a/src/toolchains.ts b/src/toolchains.ts
new file mode 100644
index 00000000..8c7d72c4
--- /dev/null
+++ b/src/toolchains.ts
@@ -0,0 +1,158 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import * as core from '@actions/core';
+import * as io from '@actions/io';
+import * as constants from './constants';
+
+import { getBooleanInput } from './util';
+import { create as xmlCreate } from 'xmlbuilder2';
+
+interface JdkInfo {
+ version: string;
+ vendor: string;
+ id: string;
+ jdkHome: string;
+}
+
+export async function configureToolchains(
+ version: string,
+ distributionName: string,
+ jdkHome: string,
+ toolchainId?: string
+) {
+ const vendor = core.getInput(constants.INPUT_MVN_TOOLCHAIN_VENDOR) || distributionName;
+ const id = toolchainId || `${vendor}_${version}`;
+ const settingsDirectory =
+ core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR);
+ const overwriteSettings = getBooleanInput(constants.INPUT_OVERWRITE_SETTINGS, true);
+
+ await createToolchainsSettings({
+ jdkInfo: {
+ version,
+ vendor,
+ id,
+ jdkHome
+ },
+ settingsDirectory,
+ overwriteSettings
+ });
+}
+
+export async function createToolchainsSettings({
+ jdkInfo,
+ settingsDirectory,
+ overwriteSettings
+}: {
+ jdkInfo: JdkInfo;
+ settingsDirectory: string;
+ overwriteSettings: boolean;
+}) {
+ core.info(
+ `Creating ${constants.MVN_TOOLCHAINS_FILE} for JDK version ${jdkInfo.version} from ${jdkInfo.vendor}`
+ );
+ // when an alternate m2 location is specified use only that location (no .m2 directory)
+ // otherwise use the home/.m2/ path
+ await io.mkdirP(settingsDirectory);
+ const originalToolchains = await readExistingToolchainsFile(settingsDirectory);
+ const updatedToolchains = generateToolchainDefinition(
+ originalToolchains,
+ jdkInfo.version,
+ jdkInfo.vendor,
+ jdkInfo.id,
+ jdkInfo.jdkHome
+ );
+ await writeToolchainsFileToDisk(settingsDirectory, updatedToolchains, overwriteSettings);
+}
+
+// only exported for testing purposes
+export function generateToolchainDefinition(
+ original: string,
+ version: string,
+ vendor: string,
+ id: string,
+ jdkHome: string
+) {
+ let xmlObj;
+ if (original?.length) {
+ xmlObj = xmlCreate(original)
+ .root()
+ .ele({
+ toolchain: {
+ type: 'jdk',
+ provides: {
+ version: `${version}`,
+ vendor: `${vendor}`,
+ id: `${id}`
+ },
+ configuration: {
+ jdkHome: `${jdkHome}`
+ }
+ }
+ });
+ } else
+ xmlObj = xmlCreate({
+ toolchains: {
+ '@xmlns': 'https://maven.apache.org/TOOLCHAINS/1.1.0',
+ '@xmlns:xsi': 'https://www.w3.org/2001/XMLSchema-instance',
+ '@xsi:schemaLocation':
+ 'https://maven.apache.org/TOOLCHAINS/1.1.0 https://maven.apache.org/xsd/toolchains-1.1.0.xsd',
+ toolchain: [
+ {
+ type: 'jdk',
+ provides: {
+ version: `${version}`,
+ vendor: `${vendor}`,
+ id: `${id}`
+ },
+ configuration: {
+ jdkHome: `${jdkHome}`
+ }
+ }
+ ]
+ }
+ });
+
+ return xmlObj.end({
+ format: 'xml',
+ wellFormed: false,
+ headless: false,
+ prettyPrint: true,
+ width: 80
+ });
+}
+
+async function readExistingToolchainsFile(directory: string) {
+ const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+ if (fs.existsSync(location)) {
+ return fs.readFileSync(location, {
+ encoding: 'utf-8',
+ flag: 'r'
+ });
+ }
+ return '';
+}
+
+async function writeToolchainsFileToDisk(
+ directory: string,
+ settings: string,
+ overwriteSettings: boolean
+) {
+ const location = path.join(directory, constants.MVN_TOOLCHAINS_FILE);
+ const settingsExists = fs.existsSync(location);
+ if (settingsExists && overwriteSettings) {
+ core.info(`Overwriting existing file ${location}`);
+ } else if (!settingsExists) {
+ core.info(`Writing to ${location}`);
+ } else {
+ core.info(
+ `Skipping generation of ${location} because file already exists and overwriting is not enabled`
+ );
+ return;
+ }
+
+ return fs.writeFileSync(location, settings, {
+ encoding: 'utf-8',
+ flag: 'w'
+ });
+}