feat: support Maven server credential origins (#1282)

* feat: support Maven server credential origins

* Refactor origin construction to include port if necessary

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: validate Maven server credential origins

* docs: note mvn-server-repository-origins requires Maven 3.10+

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Bruno Borges <bruno.borges@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Bruno Borges <brborges@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Yuna
2026-10-07 14:10:22 +02:00
committed by GitHub
parent d0e6e4dbf7
commit 2a69be27b0
9 changed files with 341 additions and 37 deletions

View File

@@ -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);
}
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 [
{
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<string, string[]>();
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(
' <server>',
` <id>${escapeXmlText(server.id)}</id>`,
` <username>${escapeXmlText(`\${env.${server.usernameEnvVar}}`)}</username>`,
` <password>${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}</password>`,
' </server>'
` <password>${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}</password>`
);
if (server.repositoryOrigins) {
lines.push(' <repositoryOrigins>');
for (const origin of server.repositoryOrigins) {
lines.push(
` <repositoryOrigin>${escapeXmlText(origin)}</repositoryOrigin>`
);
}
lines.push(' </repositoryOrigins>');
}
lines.push(' </server>');
}
lines.push(' </servers>');