diff --git a/.github/workflows/e2e-publishing.yml b/.github/workflows/e2e-publishing.yml
index 4dcab411..35c1596c 100644
--- a/.github/workflows/e2e-publishing.yml
+++ b/.github/workflows/e2e-publishing.yml
@@ -174,3 +174,115 @@ jobs:
if (-not (Test-Path $path)) {
throw "settings.xml file is not found in expected location"
}
+
+ test-maven-repositories:
+ name: Maven ${{ matrix.repositories }} repositories
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos-latest, windows-latest, ubuntu-latest]
+ repositories: [dependencies, plugins, both]
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v7
+ with:
+ persist-credentials: false
+ - name: Prepare Maven model inspection
+ run: |
+ $directory = Join-Path $env:RUNNER_TEMP "maven-repository-test"
+ New-Item -Path $directory -ItemType Directory -Force | Out-Null
+ $pomPath = Join-Path $directory "pom.xml"
+ $warmSettingsPath = Join-Path $directory "warm-settings.xml"
+ $cachePath = Join-Path $directory "repository"
+ $outputPath = Join-Path $directory "warm-effective-pom.xml"
+
+ # This project repository keeps the inspection plugin available offline
+ # after the generated settings disable inherited plugin Central.
+ @'
+
+ 4.0.0
+ setup-java.test
+ repository-settings
+ 1.0
+
+
+ inspection
+ https://repo.maven.apache.org/maven2
+
+
+
+ '@ | Set-Content -Path $pomPath
+ '' | Set-Content -Path $warmSettingsPath
+
+ mvn --batch-mode --no-transfer-progress --settings $warmSettingsPath `
+ "-Dmaven.repo.local=$cachePath" --file $pomPath `
+ org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom `
+ "-Doutput=$outputPath"
+ if ($LASTEXITCODE -ne 0) {
+ throw "Could not prepare the Maven Help Plugin"
+ }
+ - name: Configure dependency and plugin repositories
+ uses: ./
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+ settings-path: ${{ runner.temp }}/maven-repository-test/settings
+ mvn-server-credentials: |
+ private:PRIVATE_USERNAME:PRIVATE_TOKEN
+ mvn-repositories: ${{ matrix.repositories != 'plugins' && 'private:https://repo.example.invalid/maven:false' || '' }}
+ mvn-plugin-repositories: ${{ matrix.repositories != 'dependencies' && 'private:https://repo.example.invalid/maven:false' || '' }}
+ mvn-repositories-include-central: false
+ - name: Validate generated settings and effective Maven repositories
+ env:
+ REPOSITORY_CONFIGURATION: ${{ matrix.repositories }}
+ run: |
+ $directory = Join-Path $env:RUNNER_TEMP "maven-repository-test"
+ $settingsPath = Join-Path $directory "settings/settings.xml"
+ [xml]$settings = Get-Content -Path $settingsPath -Raw
+ $profiles = @($settings.settings.profiles.profile)
+ if ($profiles.Count -ne 1 -or $profiles[0].id -ne "setup-java-repositories") {
+ throw "Expected one repository profile"
+ }
+ $activeProfiles = @($settings.settings.activeProfiles.activeProfile)
+ if ($activeProfiles.Count -ne 1 -or $activeProfiles[0] -ne "setup-java-repositories") {
+ throw "Expected one active repository profile"
+ }
+ $server = $settings.settings.servers.server
+ if ($server.id -ne "private" -or $server.username -ne '${env.PRIVATE_USERNAME}' -or $server.password -ne '${env.PRIVATE_TOKEN}') {
+ throw "Repository credentials were not preserved"
+ }
+
+ function Assert-Repositories($repositories, $expectPrivate, $context) {
+ $central = @($repositories | Where-Object { $_.id -eq "central" })
+ if ($central.Count -ne 1 -or $central[0].releases.enabled -ne "false" -or $central[0].snapshots.enabled -ne "false") {
+ throw "$context did not disable inherited Maven Central"
+ }
+ $private = @($repositories | Where-Object { $_.id -eq "private" })
+ $expectedCount = if ($expectPrivate) { 1 } else { 0 }
+ if ($private.Count -ne $expectedCount) {
+ throw "$context has incorrect private repositories"
+ }
+ if ($expectPrivate -and ($private[0].url -ne "https://repo.example.invalid/maven" -or $private[0].snapshots.enabled -ne "false")) {
+ throw "$context has incorrect private repository settings"
+ }
+ }
+
+ $expectDependencies = $env:REPOSITORY_CONFIGURATION -ne "plugins"
+ $expectPlugins = $env:REPOSITORY_CONFIGURATION -ne "dependencies"
+ Assert-Repositories @($profiles[0].repositories.repository) $expectDependencies "Settings dependency repositories"
+ Assert-Repositories @($profiles[0].pluginRepositories.pluginRepository) $expectPlugins "Settings plugin repositories"
+
+ $pomPath = Join-Path $directory "pom.xml"
+ $cachePath = Join-Path $directory "repository"
+ $outputPath = Join-Path $directory "effective-pom.xml"
+ mvn --batch-mode --no-transfer-progress --offline --settings $settingsPath `
+ "-Dmaven.repo.local=$cachePath" --file $pomPath `
+ org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom `
+ "-Doutput=$outputPath"
+ if ($LASTEXITCODE -ne 0) {
+ throw "Could not inspect the effective Maven model"
+ }
+ [xml]$effectivePom = Get-Content -Path $outputPath -Raw
+ Assert-Repositories @($effectivePom.project.repositories.repository) $expectDependencies "Effective dependency repositories"
+ Assert-Repositories @($effectivePom.project.pluginRepositories.pluginRepository) $expectPlugins "Effective plugin repositories"
diff --git a/README.md b/README.md
index 18e89f28..58822e68 100644
--- a/README.md
+++ b/README.md
@@ -170,8 +170,9 @@ steps:
| `mvn-server-credentials` | Multiline Maven server credentials in the format `server-id:USERNAME_ENV:PASSWORD_ENV`. Replaces the single server configured by the three inputs above when set. | |
| `mvn-server-repository-origins` | Multiline Maven credential origins in the format `server-id:repository-origin`. Each origin must belong to a configured server ID. Requires Maven 3.10+. | |
| `mvn-repositories` | Multiline Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`. | |
-| `mvn-repositories-include-central` | Include Maven Central in the generated dependency repositories profile. When `false`, Central is disabled unless an explicit `central` repository is declared. | `true` |
-| `mvn-repositories-prioritize-central` | Place Maven Central before custom dependency repositories. Has no effect when Maven Central is excluded. | `true` |
+| `mvn-plugin-repositories` | Multiline Maven plugin repositories in the format `repository-id:repository-url:snapshots-enabled`. Can be used independently of `mvn-repositories`. | |
+| `mvn-repositories-include-central` | Include Maven Central for dependency and plugin repositories in the generated profile. When `false`, Central is disabled for both types unless that type explicitly declares `central`. Has no effect when neither repository list is configured. | `true` |
+| `mvn-repositories-prioritize-central` | Place Maven Central before custom dependency and plugin repositories. Has no effect when Central is excluded or neither repository list is configured. | `true` |
| `settings-path` | Directory where `settings.xml` is written. | `~/.m2` |
| `overwrite-settings` | Overwrite an existing `settings.xml`. | `true` |
| `gpg-private-key` | GPG private key to import into an isolated temporary keyring. | |
@@ -482,10 +483,20 @@ steps:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
-For dependencies hosted outside Maven Central, use `mvn-repositories` to add
-resolution repositories to an active profile in the generated `settings.xml`.
-Repository IDs can match `mvn-server-credentials` IDs when authentication is
-required. See [Resolving Maven dependencies from custom repositories](docs/advanced-usage.md#resolving-maven-dependencies-from-custom-repositories).
+For dependencies hosted outside Maven Central, use `mvn-repositories`; for build
+plugins, use `mvn-plugin-repositories`. Either input adds its own repository type
+to the same active profile in the generated `settings.xml`. Repository IDs can
+match `mvn-server-credentials` IDs when authentication is required.
+See [Resolving Maven dependencies from custom repositories](docs/advanced-usage.md#resolving-maven-dependencies-from-custom-repositories)
+and [Resolving Maven plugins from custom repositories](docs/advanced-usage.md#resolving-maven-plugins-from-custom-repositories).
+
+> [!WARNING]
+> `mvn-repositories-include-central: false` now disables inherited Maven Central
+> for **both dependencies and plugins**, even in existing workflows that only
+> configure `mvn-repositories`. Builds relying on uncached plugins from Central
+> must configure `mvn-plugin-repositories` with an accessible plugin repository
+> or explicitly declare `central` there. Neither repository list configured
+> means the Central controls remain inactive.
### GPG signing
diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts
index ecb230a2..7af82c8f 100644
--- a/__tests__/auth.test.ts
+++ b/__tests__/auth.test.ts
@@ -63,12 +63,51 @@ const credentials = (id: string, username: string, password: string) => [
const repositorySettings = (
repositories: auth.MavenRepository[],
includeCentral = true,
- prioritizeCentral = true
+ prioritizeCentral = true,
+ pluginRepositories: auth.MavenRepository[] = []
): auth.MavenRepositorySettings => ({
repositories,
+ pluginRepositories,
includeCentral,
prioritizeCentral
});
+const repositoryInputs = ['mvn-repositories', 'mvn-plugin-repositories'];
+const privateRepository = {
+ id: 'private',
+ url: 'https://repo.example.com/maven',
+ snapshotsEnabled: true
+};
+const privateRepositoryXml = {
+ id: privateRepository.id,
+ url: privateRepository.url,
+ snapshots: {enabled: 'true'}
+};
+const centralRepositoryXml = {
+ id: 'central',
+ url: 'https://repo.maven.apache.org/maven2',
+ snapshots: {enabled: 'false'}
+};
+const disabledCentralRepositoryXml = {
+ ...centralRepositoryXml,
+ releases: {enabled: 'false'}
+};
+const repositoryConfigurations = [
+ {
+ name: 'dependency-only',
+ repositories: [privateRepository],
+ pluginRepositories: []
+ },
+ {
+ name: 'plugin-only',
+ repositories: [],
+ pluginRepositories: [privateRepository]
+ },
+ {
+ name: 'combined',
+ repositories: [privateRepository],
+ pluginRepositories: [privateRepository]
+ }
+];
describe('auth tests', () => {
let spyOSHomedir: any;
@@ -475,6 +514,87 @@ describe('auth tests', () => {
).toEqual(['first', 'second', 'central']);
});
+ it.each([
+ {
+ name: 'Central first by default',
+ prioritizeCentral: undefined,
+ expectedRepositories: [
+ {
+ id: 'central',
+ url: 'https://repo.maven.apache.org/maven2',
+ snapshots: {enabled: 'false'}
+ },
+ {
+ id: 'first',
+ url: 'https://first.example.com/plugins',
+ snapshots: {enabled: 'false'}
+ },
+ {
+ id: 'second',
+ url: 'https://second.example.com/plugins',
+ snapshots: {enabled: 'true'}
+ }
+ ]
+ },
+ {
+ name: 'custom plugins before Central',
+ prioritizeCentral: false,
+ expectedRepositories: [
+ {
+ id: 'first',
+ url: 'https://first.example.com/plugins',
+ snapshots: {enabled: 'false'}
+ },
+ {
+ id: 'second',
+ url: 'https://second.example.com/plugins',
+ snapshots: {enabled: 'true'}
+ },
+ {
+ id: 'central',
+ url: 'https://repo.maven.apache.org/maven2',
+ snapshots: {enabled: 'false'}
+ }
+ ]
+ }
+ ])(
+ 'generates plugin-only settings with $name',
+ ({prioritizeCentral, expectedRepositories}) => {
+ const xml = auth.generate(
+ [],
+ undefined,
+ repositorySettings([], true, prioritizeCentral, [
+ {
+ id: 'first',
+ url: 'https://first.example.com/plugins',
+ snapshotsEnabled: false
+ },
+ {
+ id: 'second',
+ url: 'https://second.example.com/plugins',
+ snapshotsEnabled: true
+ }
+ ])
+ );
+
+ expect(parseXmlObject(xml)).toEqual(
+ expect.objectContaining({
+ settings: expect.objectContaining({
+ profiles: {
+ profile: {
+ id: 'setup-java-repositories',
+ pluginRepositories: {
+ pluginRepository: expectedRepositories
+ }
+ }
+ },
+ activeProfiles: {activeProfile: 'setup-java-repositories'}
+ })
+ })
+ );
+ }
+ );
+
it('excludes Central from the generated repository profile when configured', () => {
const parsed = parseXmlObject(
auth.generate(
@@ -506,6 +626,9 @@ describe('auth tests', () => {
snapshots: {enabled: 'false'}
}
]);
+ expect(
+ parsed.settings.profiles.profile.pluginRepositories.pluginRepository
+ ).toEqual(disabledCentralRepositoryXml);
});
it('combines repository and GPG configuration in shared profile blocks', () => {
@@ -543,6 +666,9 @@ describe('auth tests', () => {
snapshots: {enabled: 'false'}
}
]
+ },
+ pluginRepositories: {
+ pluginRepository: disabledCentralRepositoryXml
}
},
{
@@ -556,6 +682,161 @@ describe('auth tests', () => {
]);
});
+ it.each(
+ repositoryConfigurations.flatMap(configuration =>
+ [
+ {includeCentral: true, prioritizeCentral: true},
+ {includeCentral: true, prioritizeCentral: false},
+ {includeCentral: false, prioritizeCentral: true},
+ {includeCentral: false, prioritizeCentral: false}
+ ].map(controls => ({...configuration, ...controls}))
+ )
+ )(
+ 'generates $name repositories with includeCentral=$includeCentral and prioritizeCentral=$prioritizeCentral',
+ ({repositories, pluginRepositories, includeCentral, prioritizeCentral}) => {
+ const xml = auth.generate(
+ credentials('private', 'USERNAME', 'PASSWORD'),
+ undefined,
+ repositorySettings(
+ repositories,
+ includeCentral,
+ prioritizeCentral,
+ pluginRepositories
+ )
+ );
+ const expectedEntries = (configured: boolean) => {
+ const entries = includeCentral
+ ? configured
+ ? prioritizeCentral
+ ? [centralRepositoryXml, privateRepositoryXml]
+ : [privateRepositoryXml, centralRepositoryXml]
+ : []
+ : configured
+ ? [privateRepositoryXml, disabledCentralRepositoryXml]
+ : [disabledCentralRepositoryXml];
+ return entries.length === 1 ? entries[0] : entries;
+ };
+ const includeRepositories = repositories.length > 0 || !includeCentral;
+ const includePluginRepositories =
+ pluginRepositories.length > 0 || !includeCentral;
+
+ expect(parseXmlObject(xml)).toMatchObject({
+ settings: {
+ profiles: {
+ profile: {
+ id: 'setup-java-repositories',
+ ...(includeRepositories
+ ? {
+ repositories: {
+ repository: expectedEntries(repositories.length > 0)
+ }
+ }
+ : {}),
+ ...(includePluginRepositories
+ ? {
+ pluginRepositories: {
+ pluginRepository: expectedEntries(
+ pluginRepositories.length > 0
+ )
+ }
+ }
+ : {})
+ }
+ },
+ activeProfiles: {activeProfile: 'setup-java-repositories'}
+ }
+ });
+ if (!includeRepositories) {
+ expect(xml).not.toContain('');
+ }
+ if (!includePluginRepositories) {
+ expect(xml).not.toContain('');
+ }
+ }
+ );
+
+ it.each(repositoryConfigurations)(
+ 'handles explicit Central independently for $name repositories',
+ ({repositories, pluginRepositories}) => {
+ const central = {
+ id: 'central',
+ url: 'https://mirror.example.com/maven',
+ snapshotsEnabled: true
+ };
+ const explicitCentralXml = {
+ id: central.id,
+ url: central.url,
+ snapshots: {enabled: 'true'}
+ };
+ const xml = auth.generate(
+ [],
+ undefined,
+ repositorySettings(
+ repositories.length ? [central] : [],
+ false,
+ true,
+ pluginRepositories.length ? [central] : []
+ )
+ );
+
+ expect(parseXmlObject(xml)).toMatchObject({
+ settings: {
+ profiles: {
+ profile: {
+ repositories: {
+ repository: repositories.length
+ ? explicitCentralXml
+ : disabledCentralRepositoryXml
+ },
+ pluginRepositories: {
+ pluginRepository: pluginRepositories.length
+ ? explicitCentralXml
+ : disabledCentralRepositoryXml
+ }
+ }
+ }
+ }
+ });
+ expect(xml.match(/central<\/id>/g)).toHaveLength(2);
+ }
+ );
+
+ it('escapes plugin repository values while preserving parsed semantics', () => {
+ const repository = {
+ id: `private&<>"'é`,
+ url: `https://repo.example.com:8443/a?x=1&y="'é`,
+ snapshotsEnabled: false
+ };
+ const xml = auth.generate(
+ credentials(repository.id, 'USERNAME', 'PASSWORD'),
+ undefined,
+ repositorySettings([], true, true, [repository])
+ );
+
+ expect(parseXmlObject(xml)).toMatchObject({
+ settings: {
+ servers: {server: {id: repository.id}},
+ profiles: {
+ profile: {
+ pluginRepositories: {
+ pluginRepository: [
+ centralRepositoryXml,
+ {
+ id: repository.id,
+ url: repository.url,
+ snapshots: {enabled: 'false'}
+ }
+ ]
+ }
+ }
+ }
+ }
+ });
+ expect(xml).toContain('private&<>');
+ expect(xml).toContain('x=1&y=<value>');
+ expect(xml).not.toContain('');
+ });
+
it('escapes repository values while preserving parsed semantics', () => {
const repository = {
id: `private&<>"'é`,
@@ -601,81 +882,111 @@ describe('auth tests', () => {
]);
});
- it('parses Maven repositories while preserving colons in URLs', () => {
- expect(
- auth.parseMavenRepositories(
- [
- '',
- ' private : https://repo.example.com:8443/maven : true ',
- 'releases:https://repo.example.com/releases:false'
- ],
- true
- )
- ).toEqual([
+ it.each(repositoryInputs)(
+ 'parses %s while preserving colons in URLs',
+ inputName => {
+ expect(
+ auth.parseMavenRepositories(
+ [
+ '',
+ ' private : https://repo.example.com:8443/maven : TRUE ',
+ 'releases:https://repo.example.com/releases:false'
+ ],
+ true,
+ inputName
+ )
+ ).toEqual([
+ {
+ id: 'private',
+ url: 'https://repo.example.com:8443/maven',
+ snapshotsEnabled: true
+ },
+ {
+ id: 'releases',
+ url: 'https://repo.example.com/releases',
+ snapshotsEnabled: false
+ }
+ ]);
+ }
+ );
+
+ it.each(
+ [
{
- id: 'private',
- url: 'https://repo.example.com:8443/maven',
- snapshotsEnabled: true
+ entries: ['private'],
+ error:
+ 'Invalid mvn-repositories entry at line 1. Expected format: repository-id:repository-url:snapshots-enabled'
},
{
- id: 'releases',
- url: 'https://repo.example.com/releases',
- snapshotsEnabled: false
+ entries: ['private:https://repo.example.com'],
+ error:
+ "Invalid snapshots-enabled value '//repo.example.com' in mvn-repositories entry at line 1. Expected true or false"
+ },
+ {
+ entries: ['private::true'],
+ error:
+ 'Invalid mvn-repositories entry at line 1. repository-id, repository URL, and snapshots-enabled are required'
+ },
+ {
+ entries: ['private:https://repo.example.com:'],
+ error:
+ 'Invalid mvn-repositories entry at line 1. repository-id, repository URL, and snapshots-enabled are required'
+ },
+ {
+ entries: ['', 'private:https://repo.example.com:true', ' :url:false'],
+ error:
+ 'Invalid mvn-repositories entry at line 3. repository-id, repository URL, and snapshots-enabled are required'
+ },
+ {
+ entries: ['private:https://repo.example.com:sometimes'],
+ error:
+ "Invalid snapshots-enabled value 'sometimes' in mvn-repositories entry at line 1. Expected true or false"
}
- ]);
+ ].flatMap(testCase =>
+ repositoryInputs.map(inputName => ({
+ ...testCase,
+ inputName,
+ error: testCase.error.replace('mvn-repositories', inputName)
+ }))
+ )
+ )('rejects malformed $inputName: $entries', ({entries, error, inputName}) => {
+ expect(() => auth.parseMavenRepositories(entries, true, inputName)).toThrow(
+ error
+ );
});
- it.each([
- {
- entries: ['private'],
- error:
- 'Invalid mvn-repositories entry at line 1. Expected format: repository-id:repository-url:snapshots-enabled'
- },
- {
- entries: ['private:https://repo.example.com'],
- error:
- "Invalid snapshots-enabled value '//repo.example.com' in mvn-repositories entry at line 1. Expected true or false"
- },
- {
- entries: ['private::true'],
- error:
- 'Invalid mvn-repositories entry at line 1. repository-id, repository URL, and snapshots-enabled are required'
- },
- {
- entries: ['private:https://repo.example.com:sometimes'],
- error:
- "Invalid snapshots-enabled value 'sometimes' in mvn-repositories entry at line 1. Expected true or false"
- }
- ])('rejects malformed Maven repositories: $entries', ({entries, error}) => {
- expect(() => auth.parseMavenRepositories(entries, true)).toThrow(error);
- });
-
- it('rejects duplicate Maven repository ids', () => {
+ it.each(repositoryInputs)('rejects duplicate IDs in %s', inputName => {
expect(() =>
auth.parseMavenRepositories(
[
'private:https://first.example.com:false',
'private:https://second.example.com:true'
],
- true
+ true,
+ inputName
)
- ).toThrow("Duplicate repository-id 'private' in mvn-repositories input");
+ ).toThrow(`Duplicate repository-id 'private' in ${inputName} input`);
});
- it('reserves the Central repository id only when automatic Central inclusion is enabled', () => {
- const central = 'central:https://custom.example.com/maven:false';
+ it.each(repositoryInputs)(
+ 'reserves the Central ID in %s only when automatic inclusion is enabled',
+ inputName => {
+ const central = 'central:https://custom.example.com/maven:false';
- expect(() => auth.parseMavenRepositories([central], true)).toThrow(
- "Repository-id 'central' is reserved when mvn-repositories-include-central is enabled"
- );
- expect(auth.parseMavenRepositories([central], false)).toEqual([
- {
- id: 'central',
- url: 'https://custom.example.com/maven',
- snapshotsEnabled: false
- }
- ]);
- });
+ expect(() =>
+ auth.parseMavenRepositories([central], true, inputName)
+ ).toThrow(
+ "Repository-id 'central' is reserved when mvn-repositories-include-central is enabled"
+ );
+ expect(auth.parseMavenRepositories([central], false, inputName)).toEqual([
+ {
+ id: 'central',
+ url: 'https://custom.example.com/maven',
+ snapshotsEnabled: false
+ }
+ ]);
+ }
+ );
it('uses an explicit Central repository instead of adding a disabled override', () => {
const parsed = parseXmlObject(
@@ -721,16 +1032,138 @@ describe('auth tests', () => {
snapshotsEnabled: true
}
],
+ pluginRepositories: [],
includeCentral: false,
prioritizeCentral: false
});
});
it('does not read repository controls when no repositories are configured', () => {
+ (
+ core.getInput as jest.MockedFunction
+ ).mockReturnValue('false');
expect(auth.getMavenRepositorySettings()).toBeUndefined();
expect(core.getInput).not.toHaveBeenCalled();
});
+ it('ignores blank dependency and plugin repository inputs', () => {
+ (
+ core.getMultilineInput as jest.MockedFunction<
+ typeof core.getMultilineInput
+ >
+ ).mockReturnValue(['', ' ', '\t']);
+
+ expect(auth.getMavenRepositorySettings()).toBeUndefined();
+ expect(core.getInput).not.toHaveBeenCalled();
+ });
+
+ it.each(repositoryConfigurations)(
+ 'reads $name inputs with default Central controls and shared IDs',
+ ({repositories, pluginRepositories}) => {
+ (
+ core.getInput as jest.MockedFunction
+ ).mockReturnValue('');
+ (
+ core.getMultilineInput as jest.MockedFunction<
+ typeof core.getMultilineInput
+ >
+ ).mockImplementation(name => {
+ const configured =
+ name === 'mvn-repositories' ? repositories : pluginRepositories;
+ return configured.map(
+ repository =>
+ `${repository.id}:${repository.url}:${repository.snapshotsEnabled}`
+ );
+ });
+
+ expect(auth.getMavenRepositorySettings()).toEqual({
+ repositories,
+ pluginRepositories,
+ includeCentral: true,
+ prioritizeCentral: true
+ });
+ }
+ );
+
+ it.each([true, false])(
+ 'configures plugin repositories with credentials, GPG, a custom path, and overwrite=%s',
+ async overwriteSettings => {
+ const directory = path.join(m2Dir, 'custom');
+ const location = path.join(directory, MVN_SETTINGS_FILE);
+ fs.mkdirSync(directory, {recursive: true});
+ fs.writeFileSync(location, 'EXISTING SETTINGS');
+ (
+ core.getInput as jest.MockedFunction
+ ).mockImplementation(name => {
+ const inputs: Record = {
+ 'settings-path': directory,
+ 'overwrite-settings': String(overwriteSettings),
+ 'gpg-passphrase-env-var': 'GPG_PASSPHRASE',
+ 'mvn-repositories-include-central': 'false'
+ };
+ return inputs[name] ?? '';
+ });
+ (
+ core.getMultilineInput as jest.MockedFunction<
+ typeof core.getMultilineInput
+ >
+ ).mockImplementation(name => {
+ const inputs: Record = {
+ 'mvn-server-credentials': ['private:USERNAME:PASSWORD'],
+ 'mvn-server-repository-origins': ['private:https://repo.example.com'],
+ 'mvn-plugin-repositories': [
+ 'private:https://repo.example.com/maven:true'
+ ]
+ };
+ return inputs[name] ?? [];
+ });
+
+ await auth.configureAuthentication();
+
+ const xml = fs.readFileSync(location, 'utf8');
+ if (!overwriteSettings) {
+ expect(xml).toBe('EXISTING SETTINGS');
+ return;
+ }
+ expect(parseXmlObject(xml)).toMatchObject({
+ settings: {
+ servers: {
+ server: {
+ id: 'private',
+ username: '${env.USERNAME}',
+ password: '${env.PASSWORD}',
+ repositoryOrigins: {
+ repositoryOrigin: 'https://repo.example.com'
+ }
+ }
+ },
+ profiles: {
+ profile: [
+ {
+ id: 'setup-java-repositories',
+ repositories: {repository: disabledCentralRepositoryXml},
+ pluginRepositories: {
+ pluginRepository: [
+ privateRepositoryXml,
+ disabledCentralRepositoryXml
+ ]
+ }
+ },
+ {
+ id: 'setup-java-gpg',
+ properties: {'gpg.passphraseEnvName': 'GPG_PASSPHRASE'}
+ }
+ ]
+ },
+ activeProfiles: {
+ activeProfile: ['setup-java-repositories', 'setup-java-gpg']
+ }
+ }
+ });
+ expect(fs.existsSync(settingsFile)).toBe(false);
+ }
+ );
+
it.each([
{
entries: ['releases:RELEASES_USERNAME'],
diff --git a/action.yml b/action.yml
index 554d4980..eb32ec42 100644
--- a/action.yml
+++ b/action.yml
@@ -72,12 +72,15 @@ inputs:
mvn-repositories:
description: 'Multiline list of Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`.'
required: false
+ mvn-plugin-repositories:
+ description: 'Multiline list of Maven plugin repositories in the format `repository-id:repository-url:snapshots-enabled`.'
+ required: false
mvn-repositories-include-central:
- description: 'Include Maven Central in the generated dependency repositories profile. When false, Central is disabled unless an explicit repository with ID `central` is declared.'
+ description: 'Include Maven Central for dependency and plugin repositories in the generated profile. When false, Central is disabled for both types unless that type explicitly declares ID `central`. Has no effect when neither repository list is configured.'
required: false
default: true
mvn-repositories-prioritize-central:
- description: 'Place Maven Central before custom dependency repositories. Has no effect when Maven Central is excluded.'
+ description: 'Place Maven Central before custom dependency and plugin repositories. Has no effect when Maven Central is excluded or neither repository list is configured.'
required: false
default: true
settings-path:
diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js
index 77249968..03ab6fe1 100644
--- a/dist/cleanup/index.js
+++ b/dist/cleanup/index.js
@@ -31040,7 +31040,7 @@ module.exports = {
/* harmony export */ gk: () => (/* binding */ INPUT_CACHE),
/* harmony export */ wG: () => (/* binding */ INPUT_JOB_STATUS)
/* harmony export */ });
-/* unused harmony exports MACOS_JAVA_CONTENT_POSTFIX, INPUT_JAVA_VERSION, INPUT_JAVA_VERSION_FILE, INPUT_ARCHITECTURE, INPUT_JAVA_PACKAGE, INPUT_DISTRIBUTION, INPUT_JDK_FILE, INPUT_JDK_FILE_DEPRECATED, INPUT_CHECK_LATEST, INPUT_FORCE_DOWNLOAD, INPUT_SET_DEFAULT, INPUT_PROBLEM_MATCHER, INPUT_VERIFY_SIGNATURE, INPUT_VERIFY_SIGNATURE_PUBLIC_KEY, SIGNATURE_VERIFICATION_DOCUMENTATION_URL, SIGNATURE_VERIFICATION_FAILURE_HELP, INPUT_MVN_SERVER_CREDENTIALS, INPUT_MVN_SERVER_REPOSITORY_ORIGINS, INPUT_MVN_REPOSITORIES, INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL, INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL, INPUT_SERVER_ID, INPUT_SERVER_USERNAME_ENV_VAR, INPUT_SERVER_PASSWORD_ENV_VAR, INPUT_SERVER_USERNAME_DEPRECATED, INPUT_SERVER_PASSWORD_DEPRECATED, INPUT_SETTINGS_PATH, INPUT_OVERWRITE_SETTINGS, INPUT_GPG_PRIVATE_KEY, INPUT_GPG_PASSPHRASE_ENV_VAR, INPUT_GPG_PASSPHRASE_DEPRECATED, INPUT_DEFAULT_SERVER_USERNAME, INPUT_DEFAULT_SERVER_PASSWORD, INPUT_DEFAULT_GPG_PRIVATE_KEY, INPUT_DEFAULT_GPG_PASSPHRASE, MAVEN_GPG_PASSPHRASE_DEFAULT_ENV, GPG_PASSPHRASE_PROFILE_ID, MAVEN_REPOSITORIES_PROFILE_ID, MAVEN_CENTRAL_REPOSITORY_ID, MAVEN_CENTRAL_REPOSITORY_URL, INPUT_CACHE_DEPENDENCY_PATH, INPUT_CACHE_PATH, M2_DIR, MVN_SETTINGS_FILE, MVN_TOOLCHAINS_FILE, INPUT_MVN_TOOLCHAIN_ID, INPUT_MVN_TOOLCHAIN_VENDOR, INPUT_SHOW_DOWNLOAD_PROGRESS, MAVEN_ARGS_ENV, MAVEN_NO_TRANSFER_PROGRESS_FLAG, MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG, DISTRIBUTIONS_ONLY_MAJOR_VERSION */
+/* unused harmony exports MACOS_JAVA_CONTENT_POSTFIX, INPUT_JAVA_VERSION, INPUT_JAVA_VERSION_FILE, INPUT_ARCHITECTURE, INPUT_JAVA_PACKAGE, INPUT_DISTRIBUTION, INPUT_JDK_FILE, INPUT_JDK_FILE_DEPRECATED, INPUT_CHECK_LATEST, INPUT_FORCE_DOWNLOAD, INPUT_SET_DEFAULT, INPUT_PROBLEM_MATCHER, INPUT_VERIFY_SIGNATURE, INPUT_VERIFY_SIGNATURE_PUBLIC_KEY, SIGNATURE_VERIFICATION_DOCUMENTATION_URL, SIGNATURE_VERIFICATION_FAILURE_HELP, INPUT_MVN_SERVER_CREDENTIALS, INPUT_MVN_SERVER_REPOSITORY_ORIGINS, INPUT_MVN_REPOSITORIES, INPUT_MVN_PLUGIN_REPOSITORIES, INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL, INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL, INPUT_SERVER_ID, INPUT_SERVER_USERNAME_ENV_VAR, INPUT_SERVER_PASSWORD_ENV_VAR, INPUT_SERVER_USERNAME_DEPRECATED, INPUT_SERVER_PASSWORD_DEPRECATED, INPUT_SETTINGS_PATH, INPUT_OVERWRITE_SETTINGS, INPUT_GPG_PRIVATE_KEY, INPUT_GPG_PASSPHRASE_ENV_VAR, INPUT_GPG_PASSPHRASE_DEPRECATED, INPUT_DEFAULT_SERVER_USERNAME, INPUT_DEFAULT_SERVER_PASSWORD, INPUT_DEFAULT_GPG_PRIVATE_KEY, INPUT_DEFAULT_GPG_PASSPHRASE, MAVEN_GPG_PASSPHRASE_DEFAULT_ENV, GPG_PASSPHRASE_PROFILE_ID, MAVEN_REPOSITORIES_PROFILE_ID, MAVEN_CENTRAL_REPOSITORY_ID, MAVEN_CENTRAL_REPOSITORY_URL, INPUT_CACHE_DEPENDENCY_PATH, INPUT_CACHE_PATH, M2_DIR, MVN_SETTINGS_FILE, MVN_TOOLCHAINS_FILE, INPUT_MVN_TOOLCHAIN_ID, INPUT_MVN_TOOLCHAIN_VENDOR, INPUT_SHOW_DOWNLOAD_PROGRESS, MAVEN_ARGS_ENV, MAVEN_NO_TRANSFER_PROGRESS_FLAG, MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG, DISTRIBUTIONS_ONLY_MAJOR_VERSION */
const MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home';
const INPUT_JAVA_VERSION = 'java-version';
const INPUT_JAVA_VERSION_FILE = 'java-version-file';
@@ -31060,6 +31060,7 @@ const SIGNATURE_VERIFICATION_FAILURE_HELP = (/* unused pure expression or super
const INPUT_MVN_SERVER_CREDENTIALS = 'mvn-server-credentials';
const INPUT_MVN_SERVER_REPOSITORY_ORIGINS = 'mvn-server-repository-origins';
const INPUT_MVN_REPOSITORIES = 'mvn-repositories';
+const INPUT_MVN_PLUGIN_REPOSITORIES = 'mvn-plugin-repositories';
const INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL = 'mvn-repositories-include-central';
const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL = 'mvn-repositories-prioritize-central';
const INPUT_SERVER_ID = 'server-id';
diff --git a/dist/setup/81.index.js b/dist/setup/81.index.js
index 9309d61d..39f98e37 100644
--- a/dist/setup/81.index.js
+++ b/dist/setup/81.index.js
@@ -85,7 +85,7 @@ function getMavenServerSettings() {
passwordEnvVar: getInputWithDeprecatedAlias(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_PASSWORD_ENV_VAR */ .r4, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_PASSWORD_DEPRECATED */ .Vt, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_DEFAULT_SERVER_PASSWORD */ .xp)
}
];
- return addMavenServerRepositoryOrigins(servers, _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_SERVER_REPOSITORY_ORIGINS */ .gR));
+ return addMavenServerRepositoryOrigins(servers, _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_SERVER_REPOSITORY_ORIGINS */ .Pt));
}
// only exported for testing purposes
function parseMavenServerCredentials(entries) {
@@ -169,18 +169,21 @@ function addMavenServerRepositoryOrigins(servers, entries) {
// only exported for testing purposes
function getMavenRepositorySettings() {
const entries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES */ .W2);
- if (!entries.some(entry => entry.trim())) {
+ const pluginEntries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_PLUGIN_REPOSITORIES */ .gR);
+ if (!entries.some(entry => entry.trim()) &&
+ !pluginEntries.some(entry => entry.trim())) {
return undefined;
}
const includeCentral = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getBooleanInput */ .Vt)(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL */ .H5, true);
return {
repositories: parseMavenRepositories(entries, includeCentral),
+ pluginRepositories: parseMavenRepositories(pluginEntries, includeCentral, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_PLUGIN_REPOSITORIES */ .gR),
includeCentral,
prioritizeCentral: (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getBooleanInput */ .Vt)(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL */ .OT, true)
};
}
// only exported for testing purposes
-function parseMavenRepositories(entries, includeCentral) {
+function parseMavenRepositories(entries, includeCentral, inputName = _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES */ .W2) {
const repositories = [];
const repositoryIds = new Set();
entries.forEach((entry, index) => {
@@ -190,7 +193,7 @@ function parseMavenRepositories(entries, includeCentral) {
const firstSeparator = entry.indexOf(':');
const lastSeparator = entry.lastIndexOf(':');
if (firstSeparator <= 0 || lastSeparator <= firstSeparator) {
- throw new Error(`Invalid mvn-repositories entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`);
+ throw new Error(`Invalid ${inputName} entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`);
}
const id = entry.slice(0, firstSeparator).trim();
const url = entry.slice(firstSeparator + 1, lastSeparator).trim();
@@ -199,13 +202,13 @@ function parseMavenRepositories(entries, includeCentral) {
.trim()
.toLowerCase();
if (!id || !url || !snapshotsValue) {
- throw new Error(`Invalid mvn-repositories entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`);
+ throw new Error(`Invalid ${inputName} entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`);
}
if (snapshotsValue !== 'true' && snapshotsValue !== 'false') {
- throw new Error(`Invalid snapshots-enabled value '${snapshotsValue}' in mvn-repositories entry at line ${index + 1}. Expected true or false`);
+ throw new Error(`Invalid snapshots-enabled value '${snapshotsValue}' in ${inputName} entry at line ${index + 1}. Expected true or false`);
}
if (repositoryIds.has(id)) {
- throw new Error(`Duplicate repository-id '${id}' in mvn-repositories input`);
+ throw new Error(`Duplicate repository-id '${id}' in ${inputName} input`);
}
if (includeCentral && id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg) {
throw new Error(`Repository-id '${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg}' is reserved when ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL */ .H5} is enabled`);
@@ -219,6 +222,37 @@ function parseMavenRepositories(entries, includeCentral) {
});
return repositories;
}
+function generateRepositorySection(customRepositories, settings, element) {
+ if (!customRepositories.length && settings.includeCentral) {
+ return [];
+ }
+ const centralRepository = {
+ id: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg,
+ url: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_URL */ .jv,
+ snapshotsEnabled: false
+ };
+ const customCentralConfigured = customRepositories.some(repository => repository.id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg);
+ const repositories = settings.includeCentral
+ ? settings.prioritizeCentral
+ ? [centralRepository, ...customRepositories]
+ : [...customRepositories, centralRepository]
+ : customCentralConfigured
+ ? customRepositories
+ : [...customRepositories, { ...centralRepository, releasesEnabled: false }];
+ const container = element === 'repository' ? 'repositories' : 'pluginRepositories';
+ const lines = [` <${container}>`];
+ for (const repository of repositories) {
+ lines.push(` <${element}>`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}`, ...(repository.releasesEnabled === undefined
+ ? []
+ : [
+ ' ',
+ ` ${repository.releasesEnabled}`,
+ ' '
+ ]), ' ', ` ${repository.snapshotsEnabled}`, ' ', ` ${element}>`);
+ }
+ lines.push(` ${container}>`);
+ return lines;
+}
async function createAuthenticationSettings(servers, settingsDirectory, overwriteSettings, gpgPassphraseEnvVar = undefined, repositorySettings = undefined) {
_actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq(`Creating ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MVN_SETTINGS_FILE */ .vO} with server-id: ${servers.map(server => server.id).join(', ')}`);
// when an alternate m2 location is specified use only that location (no .m2 directory)
@@ -258,33 +292,7 @@ function generate(servers, gpgPassphraseEnvVar, repositorySettings) {
if (repositorySettings || includeGpgPassphraseProfile) {
lines.push(' ');
if (repositorySettings) {
- const centralRepository = {
- id: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg,
- url: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_URL */ .jv,
- snapshotsEnabled: false
- };
- const customCentralConfigured = repositorySettings.repositories.some(repository => repository.id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg);
- const repositories = repositorySettings.includeCentral
- ? repositorySettings.prioritizeCentral
- ? [centralRepository, ...repositorySettings.repositories]
- : [...repositorySettings.repositories, centralRepository]
- : customCentralConfigured
- ? repositorySettings.repositories
- : [
- ...repositorySettings.repositories,
- { ...centralRepository, releasesEnabled: false }
- ];
- lines.push(' ', ` ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}`, ' ');
- for (const repository of repositories) {
- lines.push(' ', ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}`, ...(repository.releasesEnabled === undefined
- ? []
- : [
- ' ',
- ` ${repository.releasesEnabled}`,
- ' '
- ]), ' ', ` ${repository.snapshotsEnabled}`, ' ', ' ');
- }
- lines.push(' ', ' ');
+ lines.push(' ', ` ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}`, ...generateRepositorySection(repositorySettings.repositories, repositorySettings, 'repository'), ...generateRepositorySection(repositorySettings.pluginRepositories ?? [], repositorySettings, 'pluginRepository'), ' ');
}
if (includeGpgPassphraseProfile) {
lines.push(' ', ` ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .GPG_PASSPHRASE_PROFILE_ID */ .K$}`, ' ', ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(gpgPassphraseEnvVar)}`, ' ', ' ');
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 1a178aaf..5c053664 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -31046,6 +31046,7 @@ module.exports = {
/* harmony export */ OD: () => (/* binding */ INPUT_DEFAULT_GPG_PRIVATE_KEY),
/* harmony export */ OT: () => (/* binding */ INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL),
/* harmony export */ PG: () => (/* binding */ MACOS_JAVA_CONTENT_POSTFIX),
+/* harmony export */ Pt: () => (/* binding */ INPUT_MVN_SERVER_REPOSITORY_ORIGINS),
/* harmony export */ QM: () => (/* binding */ INPUT_JAVA_VERSION),
/* harmony export */ RX: () => (/* binding */ INPUT_DEFAULT_GPG_PASSPHRASE),
/* harmony export */ TS: () => (/* binding */ INPUT_OVERWRITE_SETTINGS),
@@ -31060,7 +31061,7 @@ module.exports = {
/* harmony export */ af: () => (/* binding */ INPUT_JAVA_VERSION_FILE),
/* harmony export */ db: () => (/* binding */ INPUT_GPG_PASSPHRASE_ENV_VAR),
/* harmony export */ fd: () => (/* binding */ INPUT_SERVER_ID),
-/* harmony export */ gR: () => (/* binding */ INPUT_MVN_SERVER_REPOSITORY_ORIGINS),
+/* harmony export */ gR: () => (/* binding */ INPUT_MVN_PLUGIN_REPOSITORIES),
/* harmony export */ g_: () => (/* binding */ INPUT_DISTRIBUTION),
/* harmony export */ gk: () => (/* binding */ INPUT_CACHE),
/* harmony export */ hq: () => (/* binding */ MAVEN_REPOSITORIES_PROFILE_ID),
@@ -31109,6 +31110,7 @@ const SIGNATURE_VERIFICATION_FAILURE_HELP = `If this is a legitimate vendor sign
const INPUT_MVN_SERVER_CREDENTIALS = 'mvn-server-credentials';
const INPUT_MVN_SERVER_REPOSITORY_ORIGINS = 'mvn-server-repository-origins';
const INPUT_MVN_REPOSITORIES = 'mvn-repositories';
+const INPUT_MVN_PLUGIN_REPOSITORIES = 'mvn-plugin-repositories';
const INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL = 'mvn-repositories-include-central';
const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL = 'mvn-repositories-prioritize-central';
const INPUT_SERVER_ID = 'server-id';
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index 72a8ad96..dedca4ed 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -1003,14 +1003,92 @@ This configuration adds the following active profile to `settings.xml`:
```
-Maven Central is included first by default. Set
-`mvn-repositories-prioritize-central: false` to place custom repositories
-first, or set `mvn-repositories-include-central: false` to disable Central. The
-generated profile overrides the Central repository inherited from Maven's Super
-POM with releases and snapshots disabled. When automatic Central inclusion is
-off, the ID `central` may instead be declared explicitly in `mvn-repositories`
-to replace it with a user-specified repository; otherwise that ID is reserved
-to prevent duplicate entries.
+Maven Central is included first by default. The Central controls apply to both
+dependency and plugin repositories. Set
+`mvn-repositories-prioritize-central: false` to place custom repositories first
+within each configured list, or set `mvn-repositories-include-central: false` to
+disable Central for both types. The generated profile overrides the dependency
+and plugin Central repositories inherited from Maven's Super POM with releases
+and snapshots disabled, even if only one custom repository list is supplied.
+These controls have no effect when neither list contains a nonblank entry.
+
+When automatic Central inclusion is off, the ID `central` may instead be
+declared explicitly in `mvn-repositories` or `mvn-plugin-repositories` to replace
+it with a user-specified repository for that type. Declaring it in one list
+does not enable Central in the other. Otherwise that ID is reserved to prevent
+duplicate entries. Other repositories declared in your project's POM are not
+disabled by these controls.
+
+### Resolving Maven plugins from custom repositories
+
+Maven resolves build plugins through ``, separately from
+dependency ``. Use `mvn-plugin-repositories` to configure plugin
+resolution with the same multiline `repository-id:repository-url:snapshots-enabled`
+format. It can be supplied without `mvn-repositories`; dependency repositories
+are not automatically used as plugin repositories.
+
+For example, configure the same private repository for dependencies and plugins
+and disable inherited Maven Central for both:
+
+```yaml
+steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-java@v6
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+ mvn-server-credentials: |
+ private:PRIVATE_REPOSITORY_USERNAME:PRIVATE_REPOSITORY_TOKEN
+ mvn-repositories: |
+ private:https://maven.example.com/releases:false
+ mvn-plugin-repositories: |
+ private:https://maven.example.com/releases:false
+ mvn-repositories-include-central: false
+ - run: mvn --batch-mode verify
+ env:
+ PRIVATE_REPOSITORY_USERNAME: ${{ secrets.PRIVATE_REPOSITORY_USERNAME }}
+ PRIVATE_REPOSITORY_TOKEN: ${{ secrets.PRIVATE_REPOSITORY_TOKEN }}
+```
+
+The matching ID `private` selects the same server credentials for both types.
+IDs must be unique within each list, but can be shared across the two lists.
+Public repositories do not require a matching credential entry.
+
+Alongside dependency repositories, the active `setup-java-repositories` profile
+contains:
+
+```xml
+
+
+ private
+ https://maven.example.com/releases
+
+ false
+
+
+
+ central
+ https://repo.maven.apache.org/maven2
+
+ false
+
+
+ false
+
+
+
+```
+
+Omitting a Central override would leave the Super POM's plugin repository
+enabled. In particular, `mvn-repositories-include-central: false` now disables
+inherited plugin Central even for existing workflows that only configure
+`mvn-repositories`. To download plugins in such workflows, configure a plugin
+repository or explicitly declare `central` in `mvn-plugin-repositories`.
+
+With Central included, it is placed before custom plugin repositories by
+default; `mvn-repositories-prioritize-central: false` places it after them.
+All repository generation respects `settings-path` and `overwrite-settings`;
+an existing settings file is left untouched when `overwrite-settings: false`.
### GPG
diff --git a/package-lock.json b/package-lock.json
index a978598c..47cb35a8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4320,13 +4320,13 @@
"license": "ISC"
},
"node_modules/handlebars": {
- "version": "4.7.9",
- "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz",
- "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==",
+ "version": "4.7.10",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.10.tgz",
+ "integrity": "sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "minimist": "^1.2.5",
+ "minimist": "^1.2.8",
"neo-async": "^2.6.2",
"source-map": "^0.6.1",
"wordwrap": "^1.0.0"
diff --git a/src/auth.ts b/src/auth.ts
index 096e1fb5..91480f76 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -25,6 +25,7 @@ export interface MavenRepository {
export interface MavenRepositorySettings {
repositories: MavenRepository[];
+ pluginRepositories?: MavenRepository[];
includeCentral: boolean;
prioritizeCentral: boolean;
}
@@ -245,7 +246,13 @@ export function addMavenServerRepositoryOrigins(
export function getMavenRepositorySettings():
MavenRepositorySettings | undefined {
const entries = core.getMultilineInput(constants.INPUT_MVN_REPOSITORIES);
- if (!entries.some(entry => entry.trim())) {
+ const pluginEntries = core.getMultilineInput(
+ constants.INPUT_MVN_PLUGIN_REPOSITORIES
+ );
+ if (
+ !entries.some(entry => entry.trim()) &&
+ !pluginEntries.some(entry => entry.trim())
+ ) {
return undefined;
}
@@ -255,6 +262,11 @@ export function getMavenRepositorySettings():
);
return {
repositories: parseMavenRepositories(entries, includeCentral),
+ pluginRepositories: parseMavenRepositories(
+ pluginEntries,
+ includeCentral,
+ constants.INPUT_MVN_PLUGIN_REPOSITORIES
+ ),
includeCentral,
prioritizeCentral: getBooleanInput(
constants.INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL,
@@ -266,7 +278,8 @@ export function getMavenRepositorySettings():
// only exported for testing purposes
export function parseMavenRepositories(
entries: string[],
- includeCentral: boolean
+ includeCentral: boolean,
+ inputName = constants.INPUT_MVN_REPOSITORIES
): MavenRepository[] {
const repositories: MavenRepository[] = [];
const repositoryIds = new Set();
@@ -280,7 +293,7 @@ export function parseMavenRepositories(
const lastSeparator = entry.lastIndexOf(':');
if (firstSeparator <= 0 || lastSeparator <= firstSeparator) {
throw new Error(
- `Invalid mvn-repositories entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`
+ `Invalid ${inputName} entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`
);
}
@@ -292,18 +305,16 @@ export function parseMavenRepositories(
.toLowerCase();
if (!id || !url || !snapshotsValue) {
throw new Error(
- `Invalid mvn-repositories entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`
+ `Invalid ${inputName} entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`
);
}
if (snapshotsValue !== 'true' && snapshotsValue !== 'false') {
throw new Error(
- `Invalid snapshots-enabled value '${snapshotsValue}' in mvn-repositories entry at line ${index + 1}. Expected true or false`
+ `Invalid snapshots-enabled value '${snapshotsValue}' in ${inputName} entry at line ${index + 1}. Expected true or false`
);
}
if (repositoryIds.has(id)) {
- throw new Error(
- `Duplicate repository-id '${id}' in mvn-repositories input`
- );
+ throw new Error(`Duplicate repository-id '${id}' in ${inputName} input`);
}
if (includeCentral && id === constants.MAVEN_CENTRAL_REPOSITORY_ID) {
throw new Error(
@@ -322,6 +333,56 @@ export function parseMavenRepositories(
return repositories;
}
+function generateRepositorySection(
+ customRepositories: MavenRepository[],
+ settings: MavenRepositorySettings,
+ element: 'repository' | 'pluginRepository'
+): string[] {
+ if (!customRepositories.length && settings.includeCentral) {
+ return [];
+ }
+
+ const centralRepository: MavenRepository = {
+ id: constants.MAVEN_CENTRAL_REPOSITORY_ID,
+ url: constants.MAVEN_CENTRAL_REPOSITORY_URL,
+ snapshotsEnabled: false
+ };
+ const customCentralConfigured = customRepositories.some(
+ repository => repository.id === constants.MAVEN_CENTRAL_REPOSITORY_ID
+ );
+ const repositories = settings.includeCentral
+ ? settings.prioritizeCentral
+ ? [centralRepository, ...customRepositories]
+ : [...customRepositories, centralRepository]
+ : customCentralConfigured
+ ? customRepositories
+ : [...customRepositories, {...centralRepository, releasesEnabled: false}];
+ const container =
+ element === 'repository' ? 'repositories' : 'pluginRepositories';
+ const lines = [` <${container}>`];
+
+ for (const repository of repositories) {
+ lines.push(
+ ` <${element}>`,
+ ` ${escapeXmlText(repository.id)}`,
+ ` ${escapeXmlText(repository.url)}`,
+ ...(repository.releasesEnabled === undefined
+ ? []
+ : [
+ ' ',
+ ` ${repository.releasesEnabled}`,
+ ' '
+ ]),
+ ' ',
+ ` ${repository.snapshotsEnabled}`,
+ ' ',
+ ` ${element}>`
+ );
+ }
+ lines.push(` ${container}>`);
+ return lines;
+}
+
export async function createAuthenticationSettings(
servers: MavenServerCredentials[],
settingsDirectory: string,
@@ -389,49 +450,21 @@ export function generate(
if (repositorySettings || includeGpgPassphraseProfile) {
lines.push(' ');
if (repositorySettings) {
- const centralRepository: MavenRepository = {
- id: constants.MAVEN_CENTRAL_REPOSITORY_ID,
- url: constants.MAVEN_CENTRAL_REPOSITORY_URL,
- snapshotsEnabled: false
- };
- const customCentralConfigured = repositorySettings.repositories.some(
- repository => repository.id === constants.MAVEN_CENTRAL_REPOSITORY_ID
- );
- const repositories = repositorySettings.includeCentral
- ? repositorySettings.prioritizeCentral
- ? [centralRepository, ...repositorySettings.repositories]
- : [...repositorySettings.repositories, centralRepository]
- : customCentralConfigured
- ? repositorySettings.repositories
- : [
- ...repositorySettings.repositories,
- {...centralRepository, releasesEnabled: false}
- ];
-
lines.push(
' ',
` ${constants.MAVEN_REPOSITORIES_PROFILE_ID}`,
- ' '
+ ...generateRepositorySection(
+ repositorySettings.repositories,
+ repositorySettings,
+ 'repository'
+ ),
+ ...generateRepositorySection(
+ repositorySettings.pluginRepositories ?? [],
+ repositorySettings,
+ 'pluginRepository'
+ ),
+ ' '
);
- for (const repository of repositories) {
- lines.push(
- ' ',
- ` ${escapeXmlText(repository.id)}`,
- ` ${escapeXmlText(repository.url)}`,
- ...(repository.releasesEnabled === undefined
- ? []
- : [
- ' ',
- ` ${repository.releasesEnabled}`,
- ' '
- ]),
- ' ',
- ` ${repository.snapshotsEnabled}`,
- ' ',
- ' '
- );
- }
- lines.push(' ', ' ');
}
if (includeGpgPassphraseProfile) {
lines.push(
diff --git a/src/constants.ts b/src/constants.ts
index 6a213a59..bdba9224 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -19,6 +19,7 @@ export const INPUT_MVN_SERVER_CREDENTIALS = 'mvn-server-credentials';
export const INPUT_MVN_SERVER_REPOSITORY_ORIGINS =
'mvn-server-repository-origins';
export const INPUT_MVN_REPOSITORIES = 'mvn-repositories';
+export const INPUT_MVN_PLUGIN_REPOSITORIES = 'mvn-plugin-repositories';
export const INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL =
'mvn-repositories-include-central';
export const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL =