diff --git a/README.md b/README.md
index 5fe1cca96..1fde7c540 100644
--- a/README.md
+++ b/README.md
@@ -168,6 +168,7 @@ steps:
| `server-username-env-var` | Environment variable name for Maven repository username. | `GITHUB_ACTOR` |
| `server-password-env-var` | Environment variable name for Maven repository password or token. | `GITHUB_TOKEN` |
| `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. | |
| `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` |
diff --git a/__tests__/auth.test.ts b/__tests__/auth.test.ts
index a169451f4..ecb230a28 100644
--- a/__tests__/auth.test.ts
+++ b/__tests__/auth.test.ts
@@ -318,6 +318,21 @@ describe('auth tests', () => {
).toEqual(expectedSettings);
});
+ it('generates repository origins for a Maven server', () => {
+ const settings = auth.generate([
+ {
+ id: 'central',
+ usernameEnvVar: 'CENTRAL_USER',
+ passwordEnvVar: 'CENTRAL_PASS',
+ repositoryOrigins: ['https://central.sonatype.com']
+ }
+ ]);
+
+ expect(settings).toContain(`
+ https://central.sonatype.com
+ `);
+ });
+
it('does not add a gpg profile when the passphrase env var is the maven-gpg-plugin default', () => {
const id = 'packages';
const username = 'USER';
@@ -749,16 +764,23 @@ describe('auth tests', () => {
});
it('uses multiline Maven server credentials instead of single-server inputs', () => {
- (core.getMultilineInput as jest.Mock).mockReturnValue([
- 'releases:RELEASES_USERNAME:RELEASES_PASSWORD',
- 'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
- ]);
+ (core.getMultilineInput as jest.Mock).mockImplementation((name: string) =>
+ name === 'mvn-server-credentials'
+ ? [
+ 'releases:RELEASES_USERNAME:RELEASES_PASSWORD',
+ 'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
+ ]
+ : name === 'mvn-server-repository-origins'
+ ? ['releases:https://repo.example.com']
+ : []
+ );
expect(auth.getMavenServerSettings()).toEqual([
{
id: 'releases',
usernameEnvVar: 'RELEASES_USERNAME',
- passwordEnvVar: 'RELEASES_PASSWORD'
+ passwordEnvVar: 'RELEASES_PASSWORD',
+ repositoryOrigins: ['https://repo.example.com']
},
{
id: 'snapshots',
@@ -803,6 +825,106 @@ describe('auth tests', () => {
expect(core.warning).toHaveBeenCalledTimes(2);
});
+ it('adds normalized repository origins to matching Maven servers', () => {
+ expect(
+ auth.addMavenServerRepositoryOrigins(
+ [
+ {
+ id: 'central',
+ usernameEnvVar: 'CENTRAL_USER',
+ passwordEnvVar: 'CENTRAL_PASS'
+ },
+ {
+ id: 'packages',
+ usernameEnvVar: 'GITHUB_ACTOR',
+ passwordEnvVar: 'GITHUB_TOKEN'
+ }
+ ],
+ [
+ 'central:https://central.sonatype.com/',
+ 'central:https://central.sonatype.com',
+ 'central:https://central.sonatype.com:443',
+ 'packages:https://maven.pkg.github.com',
+ 'packages:ssh://packages.example.com:22',
+ 'packages:ftp://packages.example.com:21'
+ ]
+ )
+ ).toEqual([
+ {
+ id: 'central',
+ usernameEnvVar: 'CENTRAL_USER',
+ passwordEnvVar: 'CENTRAL_PASS',
+ repositoryOrigins: ['https://central.sonatype.com']
+ },
+ {
+ id: 'packages',
+ usernameEnvVar: 'GITHUB_ACTOR',
+ passwordEnvVar: 'GITHUB_TOKEN',
+ repositoryOrigins: [
+ 'https://maven.pkg.github.com',
+ 'ssh://packages.example.com:22',
+ 'ftp://packages.example.com:21'
+ ]
+ }
+ ]);
+ });
+
+ it.each([
+ {
+ entries: ['central'],
+ error:
+ 'Invalid mvn-server-repository-origins entry at line 1. Expected format: server-id:repository-origin'
+ },
+ {
+ entries: ['other:https://repo.example.com'],
+ error:
+ "Unknown server-id 'other' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https://repo.example.com/path'],
+ error:
+ "Invalid repository origin 'https://repo.example.com/path' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https:repo.example.com'],
+ error:
+ "Invalid repository origin 'https:repo.example.com' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https:/repo.example.com'],
+ error:
+ "Invalid repository origin 'https:/repo.example.com' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https://repo.example.com?'],
+ error:
+ "Invalid repository origin 'https://repo.example.com?' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https://repo.example.com#'],
+ error:
+ "Invalid repository origin 'https://repo.example.com#' in mvn-server-repository-origins at line 1"
+ },
+ {
+ entries: ['central:https://@repo.example.com'],
+ error:
+ "Invalid repository origin 'https://@repo.example.com' in mvn-server-repository-origins at line 1"
+ }
+ ])('rejects invalid Maven server repository origins', ({entries, error}) => {
+ expect(() =>
+ auth.addMavenServerRepositoryOrigins(
+ [
+ {
+ id: 'central',
+ usernameEnvVar: 'CENTRAL_USER',
+ passwordEnvVar: 'CENTRAL_PASS'
+ }
+ ],
+ entries
+ )
+ ).toThrow(error);
+ });
+
function xmlElementText(xml: string, tagName: string): string {
const match = new RegExp(`<${tagName}>([\\s\\S]*?)${tagName}>`).exec(xml);
expect(match).not.toBeNull();
diff --git a/action.yml b/action.yml
index 69f73ff40..5613d7cf7 100644
--- a/action.yml
+++ b/action.yml
@@ -66,6 +66,9 @@ inputs:
mvn-server-credentials:
description: 'Multiline list of Maven server credentials in the format `server-id:USERNAME_ENV:PASSWORD_ENV`. When set, replaces the single server configured by server-id, server-username-env-var, and server-password-env-var.'
required: false
+ mvn-server-repository-origins:
+ description: 'Multiline list of Maven credential origins in the format `server-id:repository-origin`. Each origin must belong to a configured server ID.'
+ required: false
mvn-repositories:
description: 'Multiline list of Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`.'
required: false
diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js
index 43b8c0acd..772499681 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_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_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';
@@ -31058,6 +31058,7 @@ const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key';
const SIGNATURE_VERIFICATION_DOCUMENTATION_URL = 'https://github.com/actions/setup-java#download-integrity-and-signatures';
const SIGNATURE_VERIFICATION_FAILURE_HELP = (/* unused pure expression or super */ null && (`If this is a legitimate vendor signing-key rotation, see ${SIGNATURE_VERIFICATION_DOCUMENTATION_URL} for instructions to configure the updated public key or temporarily disable signature verification.`));
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_REPOSITORIES_INCLUDE_CENTRAL = 'mvn-repositories-include-central';
const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL = 'mvn-repositories-prioritize-central';
diff --git a/dist/setup/81.index.js b/dist/setup/81.index.js
index 1aecd7fad..9309d61de 100644
--- a/dist/setup/81.index.js
+++ b/dist/setup/81.index.js
@@ -7,6 +7,7 @@ export const modules = {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ addMavenServerRepositoryOrigins: () => (/* binding */ addMavenServerRepositoryOrigins),
/* harmony export */ configureAuthentication: () => (/* binding */ configureAuthentication),
/* harmony export */ createAuthenticationSettings: () => (/* binding */ createAuthenticationSettings),
/* harmony export */ generate: () => (/* binding */ generate),
@@ -37,6 +38,7 @@ __webpack_require__.r(__webpack_exports__);
+const MAVEN_REPOSITORY_ORIGIN = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#@]+\/?$/;
async function configureAuthentication() {
const servers = getMavenServerSettings();
const repositorySettings = getMavenRepositorySettings();
@@ -74,16 +76,16 @@ function getInputWithDeprecatedAlias(inputName, deprecatedInputName, defaultValu
// only exported for testing purposes
function getMavenServerSettings() {
const entries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_SERVER_CREDENTIALS */ .MM);
- if (entries.some(entry => entry.trim())) {
- return parseMavenServerCredentials(entries);
- }
- return [
- {
- id: _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_ID */ .fd),
- usernameEnvVar: getInputWithDeprecatedAlias(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_ENV_VAR */ .sc, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_DEPRECATED */ .sp, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_DEFAULT_SERVER_USERNAME */ .Wj),
- 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)
- }
- ];
+ const servers = entries.some(entry => entry.trim())
+ ? parseMavenServerCredentials(entries)
+ : [
+ {
+ id: _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_ID */ .fd),
+ usernameEnvVar: getInputWithDeprecatedAlias(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_ENV_VAR */ .sc, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_DEPRECATED */ .sp, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_DEFAULT_SERVER_USERNAME */ .Wj),
+ 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));
}
// only exported for testing purposes
function parseMavenServerCredentials(entries) {
@@ -110,6 +112,61 @@ function parseMavenServerCredentials(entries) {
return servers;
}
// only exported for testing purposes
+function addMavenServerRepositoryOrigins(servers, entries) {
+ const serverIds = new Set(servers.map(server => server.id));
+ const originsByServer = new Map();
+ entries.forEach((entry, index) => {
+ if (!entry.trim()) {
+ return;
+ }
+ const separator = entry.indexOf(':');
+ if (separator <= 0 || separator === entry.length - 1) {
+ throw new Error(`Invalid mvn-server-repository-origins entry at line ${index + 1}. Expected format: server-id:repository-origin`);
+ }
+ const id = entry.slice(0, separator).trim();
+ const value = entry.slice(separator + 1).trim();
+ if (!id || !value) {
+ throw new Error(`Invalid mvn-server-repository-origins entry at line ${index + 1}. Server ID and repository origin are required`);
+ }
+ if (!serverIds.has(id)) {
+ throw new Error(`Unknown server-id '${id}' in mvn-server-repository-origins at line ${index + 1}`);
+ }
+ let url;
+ try {
+ if (!MAVEN_REPOSITORY_ORIGIN.test(value)) {
+ throw new Error();
+ }
+ url = new URL(value);
+ }
+ catch {
+ throw new Error(`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`);
+ }
+ if (!url.host ||
+ url.username ||
+ url.password ||
+ (url.pathname !== '' && url.pathname !== '/') ||
+ url.search ||
+ url.hash) {
+ throw new Error(`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`);
+ }
+ const explicitPort = /:(\d+)\/?$/.exec(value)?.[1];
+ const port = explicitPort ? Number.parseInt(explicitPort, 10) : undefined;
+ const includePort = port !== undefined &&
+ !((url.protocol === 'http:' && port === 80) ||
+ (url.protocol === 'https:' && port === 443));
+ const origin = `${url.protocol}//${url.hostname}${includePort ? `:${port}` : ''}`;
+ const origins = originsByServer.get(id) || [];
+ if (!origins.includes(origin)) {
+ origins.push(origin);
+ originsByServer.set(id, origins);
+ }
+ });
+ return servers.map(server => {
+ const repositoryOrigins = originsByServer.get(server.id);
+ return repositoryOrigins ? { ...server, repositoryOrigins } : server;
+ });
+}
+// 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())) {
@@ -187,7 +244,15 @@ function generate(servers, gpgPassphraseEnvVar, repositorySettings) {
' '
];
for (const server of servers) {
- lines.push(' ', ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}`, ' ');
+ lines.push(' ', ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}`, ` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}`);
+ if (server.repositoryOrigins) {
+ lines.push(' ');
+ for (const origin of server.repositoryOrigins) {
+ lines.push(` ${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(origin)}`);
+ }
+ lines.push(' ');
+ }
+ lines.push(' ');
}
lines.push(' ');
if (repositorySettings || includeGpgPassphraseProfile) {
diff --git a/dist/setup/index.js b/dist/setup/index.js
index 18e223de8..1a178aaf7 100644
--- a/dist/setup/index.js
+++ b/dist/setup/index.js
@@ -31060,6 +31060,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 */ g_: () => (/* binding */ INPUT_DISTRIBUTION),
/* harmony export */ gk: () => (/* binding */ INPUT_CACHE),
/* harmony export */ hq: () => (/* binding */ MAVEN_REPOSITORIES_PROFILE_ID),
@@ -31106,6 +31107,7 @@ const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key';
const SIGNATURE_VERIFICATION_DOCUMENTATION_URL = 'https://github.com/actions/setup-java#download-integrity-and-signatures';
const SIGNATURE_VERIFICATION_FAILURE_HELP = `If this is a legitimate vendor signing-key rotation, see ${SIGNATURE_VERIFICATION_DOCUMENTATION_URL} for instructions to configure the updated public key or temporarily disable signature verification.`;
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_REPOSITORIES_INCLUDE_CENTRAL = 'mvn-repositories-include-central';
const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL = 'mvn-repositories-prioritize-central';
diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md
index a00251d61..fba494cee 100644
--- a/docs/advanced-usage.md
+++ b/docs/advanced-usage.md
@@ -885,6 +885,8 @@ Use `mvn-server-credentials` to add more than one credential entry to the genera
When this input is set, it replaces the single server configured by `server-id`, `server-username-env-var`, and `server-password-env-var`.
+Use `mvn-server-repository-origins` when a Maven server credential must be allowed for an explicit repository origin. Each line has the format `server-id:repository-origin`; a server can have multiple origins. This is required by Maven 3.10 and later when the repository URL is not otherwise associated with the server ID.
+
```yaml
steps:
- uses: actions/checkout@v7
@@ -896,6 +898,9 @@ steps:
mvn-server-credentials: |
releases:RELEASES_USERNAME:RELEASES_PASSWORD
snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD
+ mvn-server-repository-origins: |
+ releases:https://central.sonatype.com
+ snapshots:https://central.sonatype.com
- name: Publish with Maven
run: mvn deploy
env:
@@ -913,11 +918,17 @@ This configuration produces the following server entries:
releases
${env.RELEASES_USERNAME}
${env.RELEASES_PASSWORD}
+
+ https://central.sonatype.com
+
snapshots
${env.SNAPSHOTS_USERNAME}
${env.SNAPSHOTS_PASSWORD}
+
+ https://central.sonatype.com
+
```
diff --git a/src/auth.ts b/src/auth.ts
index 3e20cb25b..096e1fb54 100644
--- a/src/auth.ts
+++ b/src/auth.ts
@@ -13,6 +13,7 @@ export interface MavenServerCredentials {
id: string;
usernameEnvVar: string;
passwordEnvVar: string;
+ repositoryOrigins?: string[];
}
export interface MavenRepository {
@@ -28,6 +29,8 @@ export interface MavenRepositorySettings {
prioritizeCentral: boolean;
}
+const MAVEN_REPOSITORY_ORIGIN = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#@]+\/?$/;
+
export async function configureAuthentication() {
const servers = getMavenServerSettings();
const repositorySettings = getMavenRepositorySettings();
@@ -95,25 +98,28 @@ export function getMavenServerSettings(): MavenServerCredentials[] {
constants.INPUT_MVN_SERVER_CREDENTIALS
);
- if (entries.some(entry => entry.trim())) {
- return parseMavenServerCredentials(entries);
- }
-
- return [
- {
- id: core.getInput(constants.INPUT_SERVER_ID),
- usernameEnvVar: getInputWithDeprecatedAlias(
- constants.INPUT_SERVER_USERNAME_ENV_VAR,
- constants.INPUT_SERVER_USERNAME_DEPRECATED,
- constants.INPUT_DEFAULT_SERVER_USERNAME
- ),
- passwordEnvVar: getInputWithDeprecatedAlias(
- constants.INPUT_SERVER_PASSWORD_ENV_VAR,
- constants.INPUT_SERVER_PASSWORD_DEPRECATED,
- constants.INPUT_DEFAULT_SERVER_PASSWORD
- )
- }
- ];
+ const servers = entries.some(entry => entry.trim())
+ ? parseMavenServerCredentials(entries)
+ : [
+ {
+ id: core.getInput(constants.INPUT_SERVER_ID),
+ usernameEnvVar: getInputWithDeprecatedAlias(
+ constants.INPUT_SERVER_USERNAME_ENV_VAR,
+ constants.INPUT_SERVER_USERNAME_DEPRECATED,
+ constants.INPUT_DEFAULT_SERVER_USERNAME
+ ),
+ passwordEnvVar: getInputWithDeprecatedAlias(
+ constants.INPUT_SERVER_PASSWORD_ENV_VAR,
+ constants.INPUT_SERVER_PASSWORD_DEPRECATED,
+ constants.INPUT_DEFAULT_SERVER_PASSWORD
+ )
+ }
+ ];
+
+ return addMavenServerRepositoryOrigins(
+ servers,
+ core.getMultilineInput(constants.INPUT_MVN_SERVER_REPOSITORY_ORIGINS)
+ );
}
// only exported for testing purposes
@@ -156,6 +162,85 @@ export function parseMavenServerCredentials(
return servers;
}
+// only exported for testing purposes
+export function addMavenServerRepositoryOrigins(
+ servers: MavenServerCredentials[],
+ entries: string[]
+): MavenServerCredentials[] {
+ const serverIds = new Set(servers.map(server => server.id));
+ const originsByServer = new Map();
+
+ entries.forEach((entry, index) => {
+ if (!entry.trim()) {
+ return;
+ }
+
+ const separator = entry.indexOf(':');
+ if (separator <= 0 || separator === entry.length - 1) {
+ throw new Error(
+ `Invalid mvn-server-repository-origins entry at line ${index + 1}. Expected format: server-id:repository-origin`
+ );
+ }
+
+ const id = entry.slice(0, separator).trim();
+ const value = entry.slice(separator + 1).trim();
+ if (!id || !value) {
+ throw new Error(
+ `Invalid mvn-server-repository-origins entry at line ${index + 1}. Server ID and repository origin are required`
+ );
+ }
+ if (!serverIds.has(id)) {
+ throw new Error(
+ `Unknown server-id '${id}' in mvn-server-repository-origins at line ${index + 1}`
+ );
+ }
+
+ let url: URL;
+ try {
+ if (!MAVEN_REPOSITORY_ORIGIN.test(value)) {
+ throw new Error();
+ }
+ url = new URL(value);
+ } catch {
+ throw new Error(
+ `Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`
+ );
+ }
+ if (
+ !url.host ||
+ url.username ||
+ url.password ||
+ (url.pathname !== '' && url.pathname !== '/') ||
+ url.search ||
+ url.hash
+ ) {
+ throw new Error(
+ `Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`
+ );
+ }
+
+ const explicitPort = /:(\d+)\/?$/.exec(value)?.[1];
+ const port = explicitPort ? Number.parseInt(explicitPort, 10) : undefined;
+ const includePort =
+ port !== undefined &&
+ !(
+ (url.protocol === 'http:' && port === 80) ||
+ (url.protocol === 'https:' && port === 443)
+ );
+ const origin = `${url.protocol}//${url.hostname}${includePort ? `:${port}` : ''}`;
+ const origins = originsByServer.get(id) || [];
+ if (!origins.includes(origin)) {
+ origins.push(origin);
+ originsByServer.set(id, origins);
+ }
+ });
+
+ return servers.map(server => {
+ const repositoryOrigins = originsByServer.get(server.id);
+ return repositoryOrigins ? {...server, repositoryOrigins} : server;
+ });
+}
+
// only exported for testing purposes
export function getMavenRepositorySettings():
MavenRepositorySettings | undefined {
@@ -286,9 +371,18 @@ export function generate(
' ',
` ${escapeXmlText(server.id)}`,
` ${escapeXmlText(`\${env.${server.usernameEnvVar}}`)}`,
- ` ${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}`,
- ' '
+ ` ${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}`
);
+ if (server.repositoryOrigins) {
+ lines.push(' ');
+ for (const origin of server.repositoryOrigins) {
+ lines.push(
+ ` ${escapeXmlText(origin)}`
+ );
+ }
+ lines.push(' ');
+ }
+ lines.push(' ');
}
lines.push(' ');
diff --git a/src/constants.ts b/src/constants.ts
index 9404e188d..6a213a592 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -16,6 +16,8 @@ export const SIGNATURE_VERIFICATION_DOCUMENTATION_URL =
'https://github.com/actions/setup-java#download-integrity-and-signatures';
export const SIGNATURE_VERIFICATION_FAILURE_HELP = `If this is a legitimate vendor signing-key rotation, see ${SIGNATURE_VERIFICATION_DOCUMENTATION_URL} for instructions to configure the updated public key or temporarily disable signature verification.`;
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_REPOSITORIES_INCLUDE_CENTRAL =
'mvn-repositories-include-central';