Compare commits

...

1 Commits

Author SHA1 Message Date
Bruno Borges b843ebf9b9 Support Maven plugin repositories in generated settings.xml (#1286)
* Add Maven plugin repository support

Generate plugin repositories alongside dependency repositories and share Central inclusion and ordering controls. Cover repository parsing, credentials, explicit Central overrides, plugin ordering, and effective Maven models; document the compatibility change and rebuild runtime bundles.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 52a8a8e2-7965-42d3-88d9-d66f64d0d60d

* Fix CI audit by updating Handlebars to 4.7.10

Update the transitive test dependency to the patched release without changing runtime dependencies or suppressing audit checks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 282b173f-7b1d-4fcd-bca4-9c8e2fa72a37

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 52a8a8e2-7965-42d3-88d9-d66f64d0d60d
Copilot-Session: 282b173f-7b1d-4fcd-bca4-9c8e2fa72a37
2026-10-14 16:11:40 -04:00
11 changed files with 845 additions and 163 deletions
+112
View File
@@ -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.
@'
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>setup-java.test</groupId>
<artifactId>repository-settings</artifactId>
<version>1.0</version>
<pluginRepositories>
<pluginRepository>
<id>inspection</id>
<url>https://repo.maven.apache.org/maven2</url>
</pluginRepository>
</pluginRepositories>
</project>
'@ | Set-Content -Path $pomPath
'<settings />' | 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"
+17 -6
View File
@@ -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
+493 -60
View File
@@ -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('<repositories>');
}
if (!includePluginRepositories) {
expect(xml).not.toContain('<pluginRepositories>');
}
}
);
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(/<id>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=<value>"'é`,
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&amp;&lt;&gt;');
expect(xml).toContain('x=1&amp;y=&lt;value&gt;');
expect(xml).not.toContain('<repositories>');
});
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<typeof core.getInput>
).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<typeof core.getInput>
).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<typeof core.getInput>
).mockImplementation(name => {
const inputs: Record<string, string> = {
'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<string, string[]> = {
'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'],
+5 -2
View File
@@ -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:
+2 -1
View File
@@ -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';
+42 -34
View File
@@ -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}>`, ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}</id>`, ` <url>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}</url>`, ...(repository.releasesEnabled === undefined
? []
: [
' <releases>',
` <enabled>${repository.releasesEnabled}</enabled>`,
' </releases>'
]), ' <snapshots>', ` <enabled>${repository.snapshotsEnabled}</enabled>`, ' </snapshots>', ` </${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(' <profiles>');
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(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}</id>`, ' <repositories>');
for (const repository of repositories) {
lines.push(' <repository>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}</id>`, ` <url>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}</url>`, ...(repository.releasesEnabled === undefined
? []
: [
' <releases>',
` <enabled>${repository.releasesEnabled}</enabled>`,
' </releases>'
]), ' <snapshots>', ` <enabled>${repository.snapshotsEnabled}</enabled>`, ' </snapshots>', ' </repository>');
}
lines.push(' </repositories>', ' </profile>');
lines.push(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}</id>`, ...generateRepositorySection(repositorySettings.repositories, repositorySettings, 'repository'), ...generateRepositorySection(repositorySettings.pluginRepositories ?? [], repositorySettings, 'pluginRepository'), ' </profile>');
}
if (includeGpgPassphraseProfile) {
lines.push(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .GPG_PASSPHRASE_PROFILE_ID */ .K$}</id>`, ' <properties>', ` <gpg.passphraseEnvName>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(gpgPassphraseEnvVar)}</gpg.passphraseEnvName>`, ' </properties>', ' </profile>');
+3 -1
View File
@@ -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';
+86 -8
View File
@@ -1003,14 +1003,92 @@ This configuration adds the following active profile to `settings.xml`:
</activeProfiles>
```
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 `<pluginRepositories>`, separately from
dependency `<repositories>`. 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
<pluginRepositories>
<pluginRepository>
<id>private</id>
<url>https://maven.example.com/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>central</id>
<url>https://repo.maven.apache.org/maven2</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
```
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
+4 -4
View File
@@ -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"
+80 -47
View File
@@ -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<string>();
@@ -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}>`,
` <id>${escapeXmlText(repository.id)}</id>`,
` <url>${escapeXmlText(repository.url)}</url>`,
...(repository.releasesEnabled === undefined
? []
: [
' <releases>',
` <enabled>${repository.releasesEnabled}</enabled>`,
' </releases>'
]),
' <snapshots>',
` <enabled>${repository.snapshotsEnabled}</enabled>`,
' </snapshots>',
` </${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(' <profiles>');
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(
' <profile>',
` <id>${constants.MAVEN_REPOSITORIES_PROFILE_ID}</id>`,
' <repositories>'
...generateRepositorySection(
repositorySettings.repositories,
repositorySettings,
'repository'
),
...generateRepositorySection(
repositorySettings.pluginRepositories ?? [],
repositorySettings,
'pluginRepository'
),
' </profile>'
);
for (const repository of repositories) {
lines.push(
' <repository>',
` <id>${escapeXmlText(repository.id)}</id>`,
` <url>${escapeXmlText(repository.url)}</url>`,
...(repository.releasesEnabled === undefined
? []
: [
' <releases>',
` <enabled>${repository.releasesEnabled}</enabled>`,
' </releases>'
]),
' <snapshots>',
` <enabled>${repository.snapshotsEnabled}</enabled>`,
' </snapshots>',
' </repository>'
);
}
lines.push(' </repositories>', ' </profile>');
}
if (includeGpgPassphraseProfile) {
lines.push(
+1
View File
@@ -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 =