Compare commits

..

1 Commits

Author SHA1 Message Date
Yuna 2a69be27b0 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>
2026-10-07 14:10:22 +02:00
18 changed files with 435 additions and 287 deletions
+1 -3
View File
@@ -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. 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` |
@@ -233,13 +234,10 @@ Additional distribution notes:
| --- | --- |
| Major version | `8`, `11`, `17`, `21`, `25` |
| Specific feature or patch version | `11.0`, `11.0.4`, `17.0`, `8.0.282+8` |
| Exact build, with optional `jdk-` prefix | `27+35`, `27.0+35`, `27.0.0+35`, `jdk-27+35` |
| JEP 322 multi-field versions | `11.0.9.1`, `18.0.1.1`, `26.0.2.1+1` |
| Early access | `15-ea`, `15.0.0-ea`, `27-ea` |
| Latest stable GA release | `latest` |
Numeric Java versions can use a `jdk-` prefix. For example, `jdk-27+35`, `27+35`, and `27.0+35` all normalize to `27.0.0+35` and pin that exact build; `jdk-27` and `27` remain major-version ranges. The prefix does not select a distribution or package type. Release availability depends on the selected distribution and platform.
When `check-latest` is `false`, the action first tries the runner tool cache for the requested distribution, package type, architecture, and version range. It downloads Java only when no matching cached version is found. When `check-latest` is `true`, the action checks remote metadata first and downloads if the cached version is not current.
GitHub-hosted runners primarily pre-cache Eclipse Temurin JDKs. See the installed Java versions for [Ubuntu](https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md#java), [Windows](https://github.com/actions/runner-images/blob/main/images/windows/Windows2025-Readme.md#java), and [macOS](https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#java). On a fresh GitHub-hosted runner, requests for other distributions usually miss the tool cache and resolve from remote metadata. For broad version ranges such as a major version (`21`, `25`), this often behaves similarly to `check-latest: true` because the action downloads the latest available release that satisfies the range.
+127 -5
View File
@@ -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(` <repositoryOrigins>
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
</repositoryOrigins>`);
});
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();
@@ -374,49 +374,6 @@ describe('findInToolcache', () => {
);
});
describe('exact build requests in tool-cache', () => {
afterEach(() => {
jest.resetAllMocks();
});
it.each(['jdk-27+35', '27+35', '27.0+35', '27.0.0+35'])(
'only matches the requested build for %s',
input => {
const findAllVersions = jest.mocked(tc.findAllVersions);
findAllVersions.mockReturnValue([
'27.0.1-35',
'27.0.0-36',
'27.0.0-ea.35',
'27.0.0-35'
]);
jest
.mocked(util.getToolcachePath)
.mockImplementation(
(toolname, version, architecture) =>
`/hostedtoolcache/${toolname}/${version}/${architecture}`
);
const distribution = new EmptyJavaBase({
version: input,
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
});
expect(distribution['findInToolcache']()).toEqual({
version: '27.0.0+35',
path: '/hostedtoolcache/Java_Empty_jdk/27.0.0-35/x64'
});
findAllVersions.mockReturnValue([
'27.0.1-35',
'27.0.0-36',
'27.0.0-ea.35'
]);
expect(distribution['findInToolcache']()).toBeNull();
}
);
});
describe('setupJava', () => {
const actualJavaVersion = '11.0.9';
const installedJavaVersion = '11.0.8';
@@ -1792,15 +1749,6 @@ describe('normalizeVersion', () => {
const DummyJavaBase = JavaBase as any;
it.each([
['jdk-27+35', {version: '27.0.0+35', stable: true, latest: false}],
['27+35', {version: '27.0.0+35', stable: true, latest: false}],
['27.0+35', {version: '27.0.0+35', stable: true, latest: false}],
['jdk-27', {version: '27', stable: true, latest: false}],
['jdk-27.0', {version: '27.0', stable: true, latest: false}],
['jdk-27-ea', {version: '27', stable: false, latest: false}],
['jdk-27+35-ea', {version: '27.0.0+35', stable: false, latest: false}],
['jdk-27-ea.35', {version: '27.0.0+35', stable: false, latest: false}],
['jdk-26.0.2.1+1', {version: '26.0.2+1.1', stable: true, latest: false}],
['11', {version: '11', stable: true, latest: false}],
['11.0', {version: '11.0', stable: true, latest: false}],
['11.0.10', {version: '11.0.10', stable: true, latest: false}],
@@ -1830,24 +1778,6 @@ describe('normalizeVersion', () => {
);
});
it.each([
'jdk-',
'jdk-latest',
'jdk-27.x',
'jdk->=27',
'jdk8u442-b06',
'temurin-27+35',
'jdk-27-ea+35',
'jdk-27+',
'jdk-27+35..1',
'27+35..1',
'jdk-27..0+35'
])('rejects malformed or unsupported release tags: %s', input => {
expect(() =>
DummyJavaBase.prototype.normalizeVersion.call(null, input)
).toThrow(/is not valid SemVer notation for a Java version/);
});
it.each(['latest-ea', 'latest.1', 'LATEST-EA', ' latest-ea '])(
'normalizeVersion should throw a targeted error for latest combined with a qualifier (%s)',
version => {
@@ -82,7 +82,7 @@ describe('getJavaDistribution', () => {
);
});
it.each(['8', '23.x', '23.0.1.1', '23.0.1.1+1', '<24', 'jdk-23+35'])(
it.each(['8', '23.x', '23.0.1.1', '23.0.1.1+1', '<24'])(
"rejects Temurin java-package 'jdk+jmods' for version %s",
async version => {
await expect(
@@ -96,15 +96,7 @@ describe('getJavaDistribution', () => {
}
);
it.each([
'24',
'24.0.1.1',
'25.0.4.1+1',
'25-ea',
'>=21',
'latest',
'jdk-27+35'
])(
it.each(['24', '24.0.1.1', '25.0.4.1+1', '25-ea', '>=21', 'latest'])(
"accepts Temurin java-package 'jdk+jmods' for version %s",
async version => {
expect(
@@ -125,17 +117,6 @@ describe('getJavaDistribution', () => {
).toBeNull();
});
it('does not change the distribution or package for a jdk-prefixed version', async () => {
const distribution = await getJavaDistribution(
JavaDistribution.Zulu,
installerOptions('jre', 'jdk-27+35')
);
expect(distribution!['distribution']).toBe('Zulu');
expect(distribution!['packageType']).toBe('jre');
expect(distribution!['version']).toBe('27.0.0+35');
});
it.each(['adopt', 'adopt-hotspot', 'adopt-openj9'])(
'does not support legacy Adopt distribution %s',
async distributionName => {
@@ -378,7 +378,7 @@ describe('findPackageForDownload', () => {
expect(resolvedVersion.version).toBe('16.0.2+7');
});
describe('OpenJDK versions and release tags', () => {
describe('OpenJDK patch (respin) versions', () => {
const makeRelease = (
semverVersion: string,
openjdkVersion: string,
@@ -402,21 +402,6 @@ describe('findPackageForDownload', () => {
});
const respinManifest = [
makeRelease('27.0.1+35', '27.0.1+35', {
major: 27,
security: 1,
build: 35
}),
makeRelease('27.0.0+36', '27+36', {
major: 27,
security: 0,
build: 36
}),
makeRelease('27.0.0+35', '27+35', {
major: 27,
security: 0,
build: 35
}),
makeRelease('26.0.2+101', '26.0.2.1+1', {
major: 26,
security: 2,
@@ -442,13 +427,6 @@ describe('findPackageForDownload', () => {
];
it.each([
['jdk-27+35', '27.0.0+35'],
['27+35', '27.0.0+35'],
['27.0+35', '27.0.0+35'],
['jdk-27.0.0+35', '27.0.0+35'],
['jdk-27', '27.0.1+35'],
['27', '27.0.1+35'],
['jdk-26.0.2.1+1', '26.0.2+101'],
['26.0.2.1+1', '26.0.2+101'],
['26.0.2+10', '26.0.2+10'],
['26', '26.0.2+101'],
@@ -474,27 +452,21 @@ describe('findPackageForDownload', () => {
expect(resolvedVersion).not.toHaveProperty('openjdkVersion');
});
it.each(['26.0.2.2+1', 'jdk-27+34', '27+34'])(
'does not match a non-existent build or respin: %s',
async input => {
const distribution = new TemurinDistribution(
{
version: input,
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
},
TemurinImplementation.Hotspot
);
distribution['getAvailableVersions'] = async () =>
respinManifest as any;
await expect(
distribution['findPackageForDownload'](distribution['version'])
).rejects.toThrow(
`No matching version found for SemVer '${distribution['version']}'`
);
}
);
it('does not match a non-existent respin', async () => {
const distribution = new TemurinDistribution(
{
version: '26.0.2.2+1',
architecture: 'x64',
packageType: 'jdk',
checkLatest: false
},
TemurinImplementation.Hotspot
);
distribution['getAvailableVersions'] = async () => respinManifest as any;
await expect(
distribution['findPackageForDownload'](distribution['version'])
).rejects.toThrow(/No matching version found for SemVer '26.0.2\+2.1'/);
});
});
it('version is found but binaries list is empty', async () => {
-18
View File
@@ -102,24 +102,6 @@ describe('Java platform capabilities', () => {
}
);
it.each(['jdk-27+35', '27+35', 'jdk-27-ea.35'])(
'enforces version-dependent architecture restrictions for %s',
version => {
expect(() =>
validateJavaPlatform('corretto', 'linux', 'x86', version)
).toThrow(/x86 \(<12\)/);
}
);
it.each(['jdk-11+9', '11+9', 'jdk-11-ea.9'])(
'accepts compatible numeric release tags: %s',
version => {
expect(validateJavaPlatform('corretto', 'linux', 'x86', version)).toBe(
'x86'
);
}
);
it('allows local archives on any platform and architecture', () => {
expect(validateJavaPlatform('jdkfile', 'aix', 'mips64', '21')).toBe(
'mips64'
+1 -16
View File
@@ -191,17 +191,6 @@ describe('convertVersionToSemver', () => {
describe('normalizeJavaVersionToSemver', () => {
it.each([
['jdk-27+35', '27.0.0+35'],
['27+35', '27.0.0+35'],
['27.0+35', '27.0.0+35'],
['jdk-27.0+35', '27.0.0+35'],
['jdk-27.0.0+35', '27.0.0+35'],
['jdk-27', '27'],
['jdk-27.0', '27.0'],
['jdk-27.0.1', '27.0.1'],
['27+35.0.LTS', '27.0.0+35.0.LTS'],
['jdk-26.0.2.1+1', '26.0.2+1.1'],
['jdk-11.0.9.1', '11.0.9+1'],
['17', '17'],
['17.0.8', '17.0.8'],
['17.0.8+7', '17.0.8+7'],
@@ -210,11 +199,7 @@ describe('normalizeJavaVersionToSemver', () => {
['26.0.2.1+1', '26.0.2+1.1'],
['17.0.8.1+1080.1', '17.0.8+1.1080.1'],
['>=11.0.9.1', '>=11.0.9.1'],
['17.x', '17.x'],
['>=17 <28', '>=17 <28'],
['jdk-27.x', 'jdk-27.x'],
['jdk-latest', 'jdk-latest'],
['temurin-27+35', 'temurin-27+35']
['17.x', '17.x']
])('%s -> %s', (input: string, expected: string) => {
expect(normalizeJavaVersionToSemver(input)).toBe(expected);
});
+3
View File
@@ -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. Requires Maven 3.10+.'
required: false
mvn-repositories:
description: 'Multiline list of Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`.'
required: false
+9 -16
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_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';
@@ -31514,26 +31515,18 @@ function convertVersionToSemver(version) {
return mainVersion;
}
/**
* Normalize numeric Java versions, optionally prefixed with 'jdk-'. Pad
* abbreviated build requests ('27+35' -> '27.0.0+35') to preserve exact
* matching, and move extra JEP 322 fields into build metadata
* ('26.0.2.1+1' -> '26.0.2+1.1'). Leave ranges unchanged.
* Java versions (JEP 322) can contain more numeric fields than SemVer allows,
* e.g. '11.0.9.1' or Temurin respins such as '26.0.2.1+1'. Move the extra
* fields into SemVer build metadata ('11.0.9+1', '26.0.2+1.1'). Any other
* input (ranges, regular SemVer versions) is returned unchanged.
*/
function normalizeJavaVersionToSemver(version) {
const match = /^(?:jdk-)?(\d+(?:\.\d+)*)(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
const match = /^(\d+(?:\.\d+){3,})(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
if (!match) {
return version;
}
const versionParts = match[1].split('.');
if (match[2]) {
while (versionParts.length < 3) {
versionParts.push('0');
}
}
const converted = convertVersionToSemver(versionParts.join('.'));
return match[2]
? `${converted}${versionParts.length > 3 ? '.' : '+'}${match[2]}`
: converted;
const converted = convertVersionToSemver(match[1]);
return match[2] ? `${converted}.${match[2]}` : converted;
}
/**
* Builds a validator for the bytes currently served by a URL from the response
+4 -2
View File
@@ -728,8 +728,10 @@ class JavaBase {
version = version.replace('-ea.', '+');
stable = false;
}
// Normalize Java release tags and JEP 322 versions without losing exact
// build constraints ('jdk-27+35' -> '27.0.0+35'). Leave ranges unchanged.
// Java uses a versioning scheme (JEP 322) that can contain more numeric
// fields than SemVer allows, e.g. '18.0.1.1' or '11.0.9.1'. Convert such
// exact versions to SemVer build notation ('18.0.1+1', or '26.0.2+1.1'
// for '26.0.2.1+1') so they are accepted. Ranges are left untouched.
version = (0,util/* normalizeJavaVersionToSemver */.zZ)(version);
if (!semver_default().validRange(version)) {
throw new Error(`The string '${version}' is not valid SemVer notation for a Java version. Please check README file for code snippets and more detailed information`);
+76 -11
View File
@@ -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) {
' <servers>'
];
for (const server of servers) {
lines.push(' <server>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}</id>`, ` <username>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}</username>`, ` <password>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}</password>`, ' </server>');
lines.push(' <server>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}</id>`, ` <username>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}</username>`, ` <password>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}</password>`);
if (server.repositoryOrigins) {
lines.push(' <repositoryOrigins>');
for (const origin of server.repositoryOrigins) {
lines.push(` <repositoryOrigin>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(origin)}</repositoryOrigin>`);
}
lines.push(' </repositoryOrigins>');
}
lines.push(' </server>');
}
lines.push(' </servers>');
if (repositorySettings || includeGpgPassphraseProfile) {
+40 -37
View File
@@ -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';
@@ -31278,9 +31280,7 @@ function createUnsupportedPackageError(distributionName, packageType, supportedP
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__nccwpck_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(2088);
/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__nccwpck_require__.n(semver__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(4527);
/* harmony import */ var _package_types_js__WEBPACK_IMPORTED_MODULE_3__ = __nccwpck_require__(7835);
/* harmony import */ var _package_types_js__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(7835);
@@ -31294,21 +31294,21 @@ const STANDARD_LINUX = [
's390x'
];
const JAVA_PLATFORM_CAPABILITIES = {
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Temurin]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Temurin]: {
platforms: {
linux: [...STANDARD_LINUX, { architecture: 'armv7', versionRange: '<18' }],
macos: X64_ARM64,
windows: ['x64', 'x86', 'aarch64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Zulu]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Zulu]: {
platforms: {
linux: ['x64', 'x86', 'armv7', 'aarch64'],
macos: X64_ARM64,
windows: ['x64', 'x86', 'aarch64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Liberica]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Liberica]: {
platforms: {
linux: ['x64', 'x86', 'armv7', 'aarch64', 'ppc64le'],
macos: X64_ARM64,
@@ -31316,31 +31316,31 @@ const JAVA_PLATFORM_CAPABILITIES = {
solaris: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.LibericaNik]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.LibericaNik]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: X64_ARM64
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.JdkFile]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.JdkFile]: {
unrestricted: true
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Microsoft]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Microsoft]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: X64_ARM64
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Semeru]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Semeru]: {
platforms: {
linux: ['x64', 'x86', 'ppc64le', 'ppc64', 's390x', 'aarch64'],
macos: X64_ARM64,
windows: ['x64', 'aarch64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Corretto]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Corretto]: {
platforms: {
linux: [
'x64',
@@ -31352,62 +31352,62 @@ const JAVA_PLATFORM_CAPABILITIES = {
windows: ['x64', { architecture: 'x86', versionRange: '<12' }]
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Oracle]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Oracle]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Dragonwell]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Dragonwell]: {
platforms: {
linux: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.SapMachine]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.SapMachine]: {
platforms: {
linux: ['x64', 'aarch64', 'ppc64le'],
macos: X64_ARM64,
windows: X64_ARM64
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.GraalVM]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.GraalVM]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.GraalVMCommunity]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.GraalVMCommunity]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.JetBrains]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.JetBrains]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: X64_ARM64
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.Kona]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.Kona]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.OracleOpenJdk]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.OracleOpenJdk]: {
platforms: {
linux: X64_ARM64,
macos: X64_ARM64,
windows: ['x64']
}
},
[_package_types_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaDistribution */ .zS.RedHat]: {
[_package_types_js__WEBPACK_IMPORTED_MODULE_2__/* .JavaDistribution */ .zS.RedHat]: {
platforms: {
linux: [
'x64',
@@ -31494,15 +31494,26 @@ function isJavaDistribution(value) {
return Object.prototype.hasOwnProperty.call(JAVA_PLATFORM_CAPABILITIES, value);
}
function isVersionCompatible(version, supportedRange) {
const normalizedVersion = version.trim().toLowerCase();
let normalizedVersion = version.trim().toLowerCase();
if (normalizedVersion === 'latest') {
return true;
}
const requestedRange = semver__WEBPACK_IMPORTED_MODULE_1___default().validRange((0,_util_js__WEBPACK_IMPORTED_MODULE_2__/* .normalizeJavaVersionToSemver */ .zZ)(normalizedVersion.replace(/-ea$/, '').replace('-ea.', '+')));
if (/^\d+(\.\d+){3,}$/.test(normalizedVersion)) {
normalizedVersion = normalizeExtendedVersionToSemver(normalizedVersion);
}
const requestedRange = semver__WEBPACK_IMPORTED_MODULE_1___default().validRange(normalizedVersion.replace(/-ea$/, ''));
const capabilityRange = semver__WEBPACK_IMPORTED_MODULE_1___default().validRange(supportedRange);
if (!requestedRange || !capabilityRange) {
return true;
}
function normalizeExtendedVersionToSemver(version) {
const versionParts = version.split('.');
const mainVersion = versionParts.slice(0, 3).join('.');
if (versionParts.length > 3) {
return `${mainVersion}+${versionParts.slice(3).join('.')}`;
}
return version;
}
return semver__WEBPACK_IMPORTED_MODULE_1___default().intersects(requestedRange, capabilityRange, {
includePrerelease: true
});
@@ -31966,26 +31977,18 @@ function convertVersionToSemver(version) {
return mainVersion;
}
/**
* Normalize numeric Java versions, optionally prefixed with 'jdk-'. Pad
* abbreviated build requests ('27+35' -> '27.0.0+35') to preserve exact
* matching, and move extra JEP 322 fields into build metadata
* ('26.0.2.1+1' -> '26.0.2+1.1'). Leave ranges unchanged.
* Java versions (JEP 322) can contain more numeric fields than SemVer allows,
* e.g. '11.0.9.1' or Temurin respins such as '26.0.2.1+1'. Move the extra
* fields into SemVer build metadata ('11.0.9+1', '26.0.2+1.1'). Any other
* input (ranges, regular SemVer versions) is returned unchanged.
*/
function normalizeJavaVersionToSemver(version) {
const match = /^(?:jdk-)?(\d+(?:\.\d+)*)(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
const match = /^(\d+(?:\.\d+){3,})(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
if (!match) {
return version;
}
const versionParts = match[1].split('.');
if (match[2]) {
while (versionParts.length < 3) {
versionParts.push('0');
}
}
const converted = convertVersionToSemver(versionParts.join('.'));
return match[2]
? `${converted}${versionParts.length > 3 ? '.' : '+'}${match[2]}`
: converted;
const converted = convertVersionToSemver(match[1]);
return match[2] ? `${converted}.${match[2]}` : converted;
}
/**
* Builds a validator for the bytes currently served by a URL from the response
+14
View File
@@ -885,6 +885,11 @@ 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.
> [!NOTE]
> `mvn-server-repository-origins` only works with Maven 3.10 and later. Earlier Maven versions ignore the generated `repositoryOrigins` element and log an `Unrecognised tag: 'repositoryOrigins'` warning. GitHub-hosted runners currently ship Maven 3.9.x, so use the [Maven Wrapper](https://maven.apache.org/tools/wrapper/) or install Maven 3.10+ yourself to use this feature.
```yaml
steps:
- uses: actions/checkout@v7
@@ -896,6 +901,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 +921,17 @@ This configuration produces the following server entries:
<id>releases</id>
<username>${env.RELEASES_USERNAME}</username>
<password>${env.RELEASES_PASSWORD}</password>
<repositoryOrigins>
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
</repositoryOrigins>
</server>
<server>
<id>snapshots</id>
<username>${env.SNAPSHOTS_USERNAME}</username>
<password>${env.SNAPSHOTS_PASSWORD}</password>
<repositoryOrigins>
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
</repositoryOrigins>
</server>
</servers>
```
+114 -20
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>');
+2
View File
@@ -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';
+4 -2
View File
@@ -673,8 +673,10 @@ export abstract class JavaBase {
stable = false;
}
// Normalize Java release tags and JEP 322 versions without losing exact
// build constraints ('jdk-27+35' -> '27.0.0+35'). Leave ranges unchanged.
// Java uses a versioning scheme (JEP 322) that can contain more numeric
// fields than SemVer allows, e.g. '18.0.1.1' or '11.0.9.1'. Convert such
// exact versions to SemVer build notation ('18.0.1+1', or '26.0.2+1.1'
// for '26.0.2.1+1') so they are accepted. Ranges are left untouched.
version = normalizeJavaVersionToSemver(version);
if (!semver.validRange(version)) {
+15 -5
View File
@@ -1,6 +1,5 @@
import fs from 'fs';
import semver from 'semver';
import {normalizeJavaVersionToSemver} from '../util.js';
import {JavaDistribution} from './package-types.js';
export type JavaPlatform = 'linux' | 'macos' | 'windows' | 'solaris';
@@ -290,20 +289,31 @@ function isJavaDistribution(value: string): value is JavaDistribution {
}
function isVersionCompatible(version: string, supportedRange: string): boolean {
const normalizedVersion = version.trim().toLowerCase();
let normalizedVersion = version.trim().toLowerCase();
if (normalizedVersion === 'latest') {
return true;
}
if (/^\d+(\.\d+){3,}$/.test(normalizedVersion)) {
normalizedVersion = normalizeExtendedVersionToSemver(normalizedVersion);
}
const requestedRange = semver.validRange(
normalizeJavaVersionToSemver(
normalizedVersion.replace(/-ea$/, '').replace('-ea.', '+')
)
normalizedVersion.replace(/-ea$/, '')
);
const capabilityRange = semver.validRange(supportedRange);
if (!requestedRange || !capabilityRange) {
return true;
}
function normalizeExtendedVersionToSemver(version: string): string {
const versionParts = version.split('.');
const mainVersion = versionParts.slice(0, 3).join('.');
if (versionParts.length > 3) {
return `${mainVersion}+${versionParts.slice(3).join('.')}`;
}
return version;
}
return semver.intersects(requestedRange, capabilityRange, {
includePrerelease: true
});
+7 -17
View File
@@ -511,28 +511,18 @@ export function convertVersionToSemver(version: number[] | string) {
}
/**
* Normalize numeric Java versions, optionally prefixed with 'jdk-'. Pad
* abbreviated build requests ('27+35' -> '27.0.0+35') to preserve exact
* matching, and move extra JEP 322 fields into build metadata
* ('26.0.2.1+1' -> '26.0.2+1.1'). Leave ranges unchanged.
* Java versions (JEP 322) can contain more numeric fields than SemVer allows,
* e.g. '11.0.9.1' or Temurin respins such as '26.0.2.1+1'. Move the extra
* fields into SemVer build metadata ('11.0.9+1', '26.0.2+1.1'). Any other
* input (ranges, regular SemVer versions) is returned unchanged.
*/
export function normalizeJavaVersionToSemver(version: string): string {
const match = /^(?:jdk-)?(\d+(?:\.\d+)*)(?:\+([0-9A-Za-z.-]+))?$/.exec(
version
);
const match = /^(\d+(?:\.\d+){3,})(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
if (!match) {
return version;
}
const versionParts = match[1].split('.');
if (match[2]) {
while (versionParts.length < 3) {
versionParts.push('0');
}
}
const converted = convertVersionToSemver(versionParts.join('.'));
return match[2]
? `${converted}${versionParts.length > 3 ? '.' : '+'}${match[2]}`
: converted;
const converted = convertVersionToSemver(match[1]);
return match[2] ? `${converted}.${match[2]}` : converted;
}
/**