From 0b51851fe15ad35cac1b2cca6eca5cc72a59e33f Mon Sep 17 00:00:00 2001 From: Sudipto Sarkar Date: Tue, 13 May 2025 01:08:03 +0530 Subject: [PATCH] Added support for configuring multiple maven repositories with separate usernames and passwords --- .github/workflows/e2e-publishing.yml | 42 +++++++++ __tests__/auth.test.ts | 81 ++++++++++++------ action.yml | 27 ++++++ dist/cleanup/index.js | 3 +- dist/setup/index.js | 86 ++++++++++++------- docs/advanced-usage.md | 79 +++++++++++++++++ src/auth.ts | 123 ++++++++++++++++----------- src/constants.ts | 1 + src/mvn.setting.definition.ts | 6 ++ 9 files changed, 345 insertions(+), 103 deletions(-) create mode 100644 src/mvn.setting.definition.ts diff --git a/.github/workflows/e2e-publishing.yml b/.github/workflows/e2e-publishing.yml index 0c0aaafa9..63ab35548 100644 --- a/.github/workflows/e2e-publishing.yml +++ b/.github/workflows/e2e-publishing.yml @@ -151,3 +151,45 @@ jobs: if (-not (Test-Path $path)) { throw "settings.xml file is not found in expected location" } + test-publishing-multiple-repositories-with-gpg-passphrase: + name: Validate settings.xml + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: setup-java + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + mvn-repositories-len: 2 + server-id-0: maven-0 + server-username-0: MAVEN_USERNAME-0 + server-password-0: MAVEN_CENTRAL_TOKEN-0 + server-id-1: maven-1 + server-username-1: MAVEN_USERNAME-1 + server-password-1: MAVEN_CENTRAL_TOKEN-1 + gpg-passphrase: MAVEN_GPG_PASSPHRASE + - name: Validate settings.xml + run: | + $xmlPath = Join-Path $HOME ".m2" "settings.xml" + Get-Content $xmlPath | ForEach-Object { Write-Host $_ } + + [xml]$xml = Get-Content $xmlPath + $servers = $xml.settings.servers.server + if (($servers[0].id -ne 'maven-0') -or ($servers[0].username -ne '${env.MAVEN_USERNAME-0}') -or ($servers[0].password -ne '${env.MAVEN_CENTRAL_TOKEN-0}')) { + throw "Generated XML file is incorrect" + } + + if (($servers[1].id -ne 'maven-1') -or ($servers[0].username -ne '${env.MAVEN_PASSWORD-1}') -or ($servers[1].password -ne '${env.MAVEN_CENTRAL_TOKEN-1}')) { + throw "Generated XML file is incorrect" + } + + if (($servers[1].id -ne 'gpg.passphrase') -or ($servers[1].passphrase -ne '${env.MAVEN_GPG_PASSPHRASE}')) { + throw "Generated XML file is incorrect" + } diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts index 06591da7a..2c67ee709 100644 --- a/__tests__/auth.test.ts +++ b/__tests__/auth.test.ts @@ -43,9 +43,7 @@ describe('auth tests', () => { await io.rmRF(altHome); // ensure it doesn't already exist await auth.createAuthenticationSettings( - id, - username, - password, + [{id, username, password}], altHome, true ); @@ -56,7 +54,7 @@ describe('auth tests', () => { expect(fs.existsSync(altHome)).toBe(true); expect(fs.existsSync(altSettingsFile)).toBe(true); expect(fs.readFileSync(altSettingsFile, 'utf-8')).toEqual( - auth.generate(id, username, password) + auth.generate([{id, username, password}]) ); await io.rmRF(altHome); @@ -68,9 +66,7 @@ describe('auth tests', () => { const password = 'TOKEN'; await auth.createAuthenticationSettings( - id, - username, - password, + [{id, username, password}], m2Dir, true ); @@ -78,7 +74,7 @@ describe('auth tests', () => { expect(fs.existsSync(m2Dir)).toBe(true); expect(fs.existsSync(settingsFile)).toBe(true); expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual( - auth.generate(id, username, password) + auth.generate([{id, username, password}]) ); }, 100000); @@ -89,18 +85,15 @@ describe('auth tests', () => { const gpgPassphrase = 'GPG'; await auth.createAuthenticationSettings( - id, - username, - password, + [{id, username, password, gpgPassphrase}], m2Dir, - true, - gpgPassphrase + true ); expect(fs.existsSync(m2Dir)).toBe(true); expect(fs.existsSync(settingsFile)).toBe(true); expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual( - auth.generate(id, username, password, gpgPassphrase) + auth.generate([{id, username, password, gpgPassphrase}]) ); }, 100000); @@ -115,9 +108,7 @@ describe('auth tests', () => { expect(fs.existsSync(settingsFile)).toBe(true); await auth.createAuthenticationSettings( - id, - username, - password, + [{id, username, password}], m2Dir, true ); @@ -125,7 +116,7 @@ describe('auth tests', () => { expect(fs.existsSync(m2Dir)).toBe(true); expect(fs.existsSync(settingsFile)).toBe(true); expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual( - auth.generate(id, username, password) + auth.generate([{id, username, password}]) ); }, 100000); @@ -140,9 +131,7 @@ describe('auth tests', () => { expect(fs.existsSync(settingsFile)).toBe(true); await auth.createAuthenticationSettings( - id, - username, - password, + [{id, username, password}], m2Dir, false ); @@ -169,7 +158,7 @@ describe('auth tests', () => { `; - expect(auth.generate(id, username, password)).toEqual(expectedSettings); + expect(auth.generate([{id, username, password}])).toEqual(expectedSettings); }); it('generates valid settings.xml with additional configuration', () => { @@ -194,8 +183,50 @@ describe('auth tests', () => { `; - expect(auth.generate(id, username, password, gpgPassphrase)).toEqual( - expectedSettings - ); + expect( + auth.generate([ + {id, username, password}, + {id: 'gpg.passphrase', gpgPassphrase: gpgPassphrase} + ]) + ).toEqual(expectedSettings); + }); + + it('generates valid settings.xml for multiple repositories', () => { + const id0 = 'packages0'; + const username0 = 'USER0'; + const password0 = '&<>"\'\'"><&0'; + const id1 = 'packages1'; + const username1 = 'USER1'; + const password1 = '&<>"\'\'"><&1'; + const gpgPassphrase = 'PASSPHRASE'; + + const expectedSettings = ` + + + ${id0} + \${env.${username0}} + \${env.&<>"''"><&0} + + + ${id1} + \${env.${username1}} + \${env.&<>"''"><&1} + + + gpg.passphrase + \${env.${gpgPassphrase}} + + +`; + + expect( + auth.generate([ + {id: id0, username: username0, password: password0}, + {id: id1, username: username1, password: password1}, + {id: 'gpg.passphrase', gpgPassphrase: gpgPassphrase} + ]) + ).toEqual(expectedSettings); }); }); diff --git a/action.yml b/action.yml index d5f46bbed..e1735f54f 100644 --- a/action.yml +++ b/action.yml @@ -41,6 +41,33 @@ inputs: authentication to the Apache Maven repository. Default is $GITHUB_TOKEN' required: false default: 'GITHUB_TOKEN' + mvn-repositories-len: + description: 'Number of Maven repositories being configured - Only applicable if more than one Maven repository is being configured' + required: false + server-id-0: + description: 'ID of the first distributionManagement repository in the pom.xml + file - Only applicable if more than one Maven repository is being configured' + required: false + server-username-0: + description: 'Environment variable name for the username for authentication + to the first Maven repository - Only applicable if more than one Maven repository is being configured' + required: false + server-password-0: + description: 'Environment variable name for password or token for + authentication to the first Maven repository - Only applicable if more than one Maven repository is being configured' + required: false + server-id-1: + description: 'ID of the second distributionManagement repository in the pom.xml + file - Only applicable if more than one Maven repository is being configured' + required: false + server-username-1: + description: 'Environment variable name for the username for authentication + to the second Maven repository - Only applicable if more than one Maven repository is being configured' + required: false + server-password-1: + description: 'Environment variable name for password or token for + authentication to the second Maven repository - Only applicable if more than one Maven repository is being configured' + required: false settings-path: description: 'Path to where the settings.xml file will be written. Default is ~/.m2.' required: false diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 5a1db9633..8362036ea 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -52046,7 +52046,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = 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_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.DISTRIBUTIONS_ONLY_MAJOR_VERSION = 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_NUM_MVN_REPOS = 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'; @@ -52055,6 +52055,7 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_NUM_MVN_REPOS = 'mvn-repositories-len'; exports.INPUT_SERVER_ID = 'server-id'; exports.INPUT_SERVER_USERNAME = 'server-username'; exports.INPUT_SERVER_PASSWORD = 'server-password'; diff --git a/dist/setup/index.js b/dist/setup/index.js index 434039045..cb08ec25f 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -77493,20 +77493,21 @@ const gpg = __importStar(__nccwpck_require__(88343)); const util_1 = __nccwpck_require__(54527); 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 numMvnRepos = core.getInput(constants.INPUT_NUM_MVN_REPOS); + const mvnSettings = []; const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR); const overwriteSettings = (0, 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) || - (gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined); - if (gpgPrivateKey) { - core.setSecret(gpgPrivateKey); + let gpgPrivateKey; + if (numMvnRepos === '' || core.getInput(constants.INPUT_GPG_PRIVATE_KEY)) { + gpgPrivateKey = populateMvnSettings(mvnSettings); } - yield createAuthenticationSettings(id, username, password, settingsDirectory, overwriteSettings, gpgPassphrase); + else { + for (let i = 0; i < parseInt(numMvnRepos); i++) { + populateMvnSettings(mvnSettings, i); + } + } + yield createAuthenticationSettings(mvnSettings, settingsDirectory, overwriteSettings); if (gpgPrivateKey) { core.info('Importing private gpg key'); const keyFingerprint = (yield gpg.importKey(gpgPrivateKey)) || ''; @@ -77515,41 +77516,67 @@ function configureAuthentication() { }); } exports.configureAuthentication = configureAuthentication; -function createAuthenticationSettings(id, username, password, settingsDirectory, overwriteSettings, gpgPassphrase = undefined) { +function populateMvnSettings(mvnSettings, idx = -1) { + const id = core.getInput(getIndexedInputName(constants.INPUT_SERVER_ID, idx)); + const username = core.getInput(getIndexedInputName(constants.INPUT_SERVER_USERNAME, idx)); + const password = core.getInput(getIndexedInputName(constants.INPUT_SERVER_PASSWORD, idx)); + if (username !== '' && password !== '') { + mvnSettings.push({ id: id, username: username, password: password }); + } + if (idx === -1) { + const gpgPrivateKey = core.getInput(getIndexedInputName(constants.INPUT_GPG_PRIVATE_KEY, idx)) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY; + const gpgPassphrase = core.getInput(getIndexedInputName(constants.INPUT_GPG_PASSPHRASE, idx)) || + (gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined); + if (gpgPrivateKey) { + core.setSecret(gpgPrivateKey); + } + if (gpgPassphrase) { + mvnSettings.push({ id: 'gpg.passphrase', gpgPassphrase: gpgPassphrase }); + return gpgPrivateKey; + } + } + return undefined; +} +function getIndexedInputName(inputName, idx) { + return inputName + (idx >= 0 ? '-' + idx : ''); +} +function createAuthenticationSettings(mvnSettings, settingsDirectory, overwriteSettings) { return __awaiter(this, void 0, void 0, function* () { - core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`); + core.info(`Creating ${constants.MVN_SETTINGS_FILE}`); // when an alternate m2 location is specified use only that location (no .m2 directory) // otherwise use the home/.m2/ path yield io.mkdirP(settingsDirectory); - yield write(settingsDirectory, generate(id, username, password, gpgPassphrase), overwriteSettings); + yield write(settingsDirectory, generate(mvnSettings), overwriteSettings); }); } exports.createAuthenticationSettings = createAuthenticationSettings; // only exported for testing purposes -function generate(id, username, password, gpgPassphrase) { +function generate(mvnSettings) { const xmlObj = { settings: { '@xmlns': 'http://maven.apache.org/SETTINGS/1.0.0', '@xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', '@xsi:schemaLocation': 'http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd', servers: { - server: [ - { - id: id, - username: `\${env.${username}}`, - password: `\${env.${password}}` - } - ] + server: [] } } }; - if (gpgPassphrase) { - const gpgServer = { - id: 'gpg.passphrase', - passphrase: `\${env.${gpgPassphrase}}` - }; - xmlObj.settings.servers.server.push(gpgServer); - } + mvnSettings.forEach(mvnSetting => { + if (mvnSetting.username && mvnSetting.password) { + xmlObj.settings.servers.server.push({ + id: mvnSetting.id, + username: `\${env.${mvnSetting.username}}`, + password: `\${env.${mvnSetting.password}}` + }); + } + if (mvnSetting.gpgPassphrase) { + xmlObj.settings.servers.server.push({ + id: mvnSetting.id, + passphrase: `\${env.${mvnSetting.gpgPassphrase}}` + }); + } + }); return (0, xmlbuilder2_1.create)(xmlObj).end({ headless: true, prettyPrint: true, @@ -77805,7 +77832,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = 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_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.DISTRIBUTIONS_ONLY_MAJOR_VERSION = 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_NUM_MVN_REPOS = 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'; @@ -77814,6 +77841,7 @@ exports.INPUT_JAVA_PACKAGE = 'java-package'; exports.INPUT_DISTRIBUTION = 'distribution'; exports.INPUT_JDK_FILE = 'jdkFile'; exports.INPUT_CHECK_LATEST = 'check-latest'; +exports.INPUT_NUM_MVN_REPOS = 'mvn-repositories-len'; exports.INPUT_SERVER_ID = 'server-id'; exports.INPUT_SERVER_USERNAME = 'server-username'; exports.INPUT_SERVER_PASSWORD = 'server-password'; diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 1b1e4feea..5de9f92e4 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -411,6 +411,85 @@ The two `settings.xml` files created from the above example look like the follow If you don't want to overwrite the `settings.xml` file, you can set `overwrite-settings: false` +### Multiple repositories + +There might be instances where you will need to change the version to/from release/snapshot. That will require specifying two maven repositories - one for release versions, one for snapshot versions. + +#### Yaml example +```yaml +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + distribution: '' + java-version: '11' + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy + env: + GITHUB_TOKEN: ${{ github.token }} # GITHUB_TOKEN is the default env for the password + + - name: Set up Apache Maven Central + uses: actions/setup-java@v4 + with: # running setup-java again overwrites the settings.xml + distribution: 'temurin' + java-version: '11' + mvn-repositories-len: 2 + server-id-0: artifactory # Value of the distributionManagement/repository/id field of the pom.xml + server-username-0: ARTIFACTORY_USERNAME # env variable for username in deploy + server-password-0: ARTIFACTORY_TOKEN # env variable for token in deploy + server-id-1: snapshot-artifactory # Value of the distributionManagement/repository/id field of the pom.xml + server-username-1: SNAPSHOT_ARTIFACTORY_USERNAME # env variable for username in deploy + server-password-1: SNAPSHOT_ARTIFACTORY_TOKEN # env variable for token in deploy + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import + gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase + + - name: Publish to Apache Maven Central + run: mvn deploy + env: + ARTIFACTORY_USERNAME: maven_username123 + ARTIFACTORY_TOKEN: ${{ secrets.ARTIFACTORY_USERNAME }} + SNAPSHOT_ARTIFACTORY_USERNAME: snapshot_maven_username123 + SNAPSHOT_ARTIFACTORY_TOKEN: ${{ secrets.SNAPSHOT_ARTIFACTORY_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} +``` + +Here `mvn-repositories-len` specifies how many artifactories we're configuring here. In this example, the value is 2. In this case, the action will look for `server-id-0`, `server-username-0`, `server-password-0`, `server-id-1`, `server-username-1` and `server-password-1`. +Depending on the value of `mvn-repositories-len`, the number of entries that will be looked for will vary. But it is looking for 0 based indexing with the max value less than the value of `mvn-repositories-len`. + +`settings.xml` file created for the deployment to the Maven Artifactory +```xml + + + + artifactory + ${env.ARTIFACTORY_USERNAME} + ${env.ARTIFACTORY_TOKEN} + + + snapshot-artifactory + ${env.SNAPSHOT_ARTIFACTORY_USERNAME} + ${env.SNAPSHOT_ARTIFACTORY_TOKEN} + + + gpg.passphrase + ${env.MAVEN_GPG_PASSPHRASE} + + + +``` + + ### Extra setup for pom.xml: The Maven GPG Plugin configuration in the pom.xml file should contain the following structure to avoid possible issues like `Inappropriate ioctl for device` or `gpg: signing failed: No such file or directory`: diff --git a/src/auth.ts b/src/auth.ts index c8ea6291c..053efc002 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -9,11 +9,11 @@ import {create as xmlCreate} from 'xmlbuilder2'; import * as constants from './constants'; import * as gpg from './gpg'; import {getBooleanInput} from './util'; +import {MvnSettingDefinition} from './mvn.setting.definition'; 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 numMvnRepos = core.getInput(constants.INPUT_NUM_MVN_REPOS); + const mvnSettings: Array = []; const settingsDirectory = core.getInput(constants.INPUT_SETTINGS_PATH) || path.join(os.homedir(), constants.M2_DIR); @@ -21,24 +21,19 @@ export async function configureAuthentication() { 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) || - (gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined); - - if (gpgPrivateKey) { - core.setSecret(gpgPrivateKey); + let gpgPrivateKey; + if (numMvnRepos === '' || core.getInput(constants.INPUT_GPG_PRIVATE_KEY)) { + gpgPrivateKey = populateMvnSettings(mvnSettings); + } else { + for (let i = 0; i < parseInt(numMvnRepos); i++) { + populateMvnSettings(mvnSettings, i); + } } await createAuthenticationSettings( - id, - username, - password, + mvnSettings, settingsDirectory, - overwriteSettings, - gpgPassphrase + overwriteSettings ); if (gpgPrivateKey) { @@ -48,32 +43,61 @@ export async function configureAuthentication() { } } +function populateMvnSettings( + mvnSettings: Array, + idx = -1 +): string | undefined { + const id = core.getInput(getIndexedInputName(constants.INPUT_SERVER_ID, idx)); + const username = core.getInput( + getIndexedInputName(constants.INPUT_SERVER_USERNAME, idx) + ); + const password = core.getInput( + getIndexedInputName(constants.INPUT_SERVER_PASSWORD, idx) + ); + if (username !== '' && password !== '') { + mvnSettings.push({id: id, username: username, password: password}); + } + + if (idx === -1) { + const gpgPrivateKey = + core.getInput( + getIndexedInputName(constants.INPUT_GPG_PRIVATE_KEY, idx) + ) || constants.INPUT_DEFAULT_GPG_PRIVATE_KEY; + const gpgPassphrase = + core.getInput(getIndexedInputName(constants.INPUT_GPG_PASSPHRASE, idx)) || + (gpgPrivateKey ? constants.INPUT_DEFAULT_GPG_PASSPHRASE : undefined); + + if (gpgPrivateKey) { + core.setSecret(gpgPrivateKey); + } + + if (gpgPassphrase) { + mvnSettings.push({id: 'gpg.passphrase', gpgPassphrase: gpgPassphrase}); + return gpgPrivateKey; + } + } + + return undefined; +} + +function getIndexedInputName(inputName: string, idx: number): string { + return inputName + (idx >= 0 ? '-' + idx : ''); +} + export async function createAuthenticationSettings( - id: string, - username: string, - password: string, + mvnSettings: Array, settingsDirectory: string, - overwriteSettings: boolean, - gpgPassphrase: string | undefined = undefined + overwriteSettings: boolean ) { - core.info(`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${id}`); + core.info(`Creating ${constants.MVN_SETTINGS_FILE}`); // when an alternate m2 location is specified use only that location (no .m2 directory) // otherwise use the home/.m2/ path await io.mkdirP(settingsDirectory); - await write( - settingsDirectory, - generate(id, username, password, gpgPassphrase), - overwriteSettings - ); + await write(settingsDirectory, generate(mvnSettings), overwriteSettings); } // only exported for testing purposes -export function generate( - id: string, - username: string, - password: string, - gpgPassphrase?: string | undefined -) { +export function generate(mvnSettings: Array) { const xmlObj: {[key: string]: any} = { settings: { '@xmlns': 'http://maven.apache.org/SETTINGS/1.0.0', @@ -81,24 +105,27 @@ export function generate( '@xsi:schemaLocation': 'http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd', servers: { - server: [ - { - id: id, - username: `\${env.${username}}`, - password: `\${env.${password}}` - } - ] + server: [] } } }; - if (gpgPassphrase) { - const gpgServer = { - id: 'gpg.passphrase', - passphrase: `\${env.${gpgPassphrase}}` - }; - xmlObj.settings.servers.server.push(gpgServer); - } + mvnSettings.forEach(mvnSetting => { + if (mvnSetting.username && mvnSetting.password) { + xmlObj.settings.servers.server.push({ + id: mvnSetting.id, + username: `\${env.${mvnSetting.username}}`, + password: `\${env.${mvnSetting.password}}` + }); + } + + if (mvnSetting.gpgPassphrase) { + xmlObj.settings.servers.server.push({ + id: mvnSetting.id, + passphrase: `\${env.${mvnSetting.gpgPassphrase}}` + }); + } + }); return xmlCreate(xmlObj).end({ headless: true, diff --git a/src/constants.ts b/src/constants.ts index 93af286f8..be326f965 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -6,6 +6,7 @@ export const INPUT_JAVA_PACKAGE = 'java-package'; export const INPUT_DISTRIBUTION = 'distribution'; export const INPUT_JDK_FILE = 'jdkFile'; export const INPUT_CHECK_LATEST = 'check-latest'; +export const INPUT_NUM_MVN_REPOS = 'mvn-repositories-len'; export const INPUT_SERVER_ID = 'server-id'; export const INPUT_SERVER_USERNAME = 'server-username'; export const INPUT_SERVER_PASSWORD = 'server-password'; diff --git a/src/mvn.setting.definition.ts b/src/mvn.setting.definition.ts new file mode 100644 index 000000000..3e2c41de9 --- /dev/null +++ b/src/mvn.setting.definition.ts @@ -0,0 +1,6 @@ +export interface MvnSettingDefinition { + id: string; + username?: string; + password?: string; + gpgPassphrase?: string; +}