mirror of
https://github.com/actions/setup-java.git
synced 2026-10-15 15:14:53 +00:00
Validate cache / sbt-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / gradle1-save (macos-latest) (push) Waiting to run
Validate cache / sbt-restore (ubuntu-22.04) (push) Blocked by required conditions
Validate cache / sbt-save (windows-latest) (push) Waiting to run
Validate cache / gradle-save (macos-15-intel) (push) Waiting to run
Validate cache / gradle-save (windows-latest) (push) Waiting to run
Validate cache / gradle-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / gradle-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / gradle-restore (windows-latest) (push) Blocked by required conditions
Validate cache / maven-save (macos-15-intel) (push) Waiting to run
Validate cache / maven-save (windows-latest) (push) Waiting to run
Validate cache / maven-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / maven-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / maven-restore (windows-latest) (push) Blocked by required conditions
Validate cache / sbt-save (macos-15-intel) (push) Waiting to run
Validate cache / sbt-restore (windows-latest) (push) Blocked by required conditions
Validate cache / gradle1-save (windows-latest) (push) Waiting to run
Validate cache / gradle1-restore (macos-latest) (push) Blocked by required conditions
Validate cache / gradle1-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / gradle1-restore (windows-latest) (push) Blocked by required conditions
Validate cache / gradle2-restore (macos-latest) (push) Blocked by required conditions
Validate cache / gradle2-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / gradle2-restore (windows-latest) (push) Blocked by required conditions
Validate cache / maven1-save (macos-15-intel) (push) Waiting to run
Validate cache / maven1-save (windows-latest) (push) Waiting to run
Validate cache / maven1-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / maven1-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / maven1-restore (windows-latest) (push) Blocked by required conditions
Validate cache / maven2-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / maven2-restore (ubuntu-latest) (push) Blocked by required conditions
Validate cache / maven2-restore (windows-latest) (push) Blocked by required conditions
Validate cache / sbt1-save (macos-15-intel) (push) Waiting to run
Validate cache / sbt1-save (windows-latest) (push) Waiting to run
Validate cache / sbt1-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / sbt1-restore (ubuntu-22.04) (push) Blocked by required conditions
Validate cache / sbt1-restore (windows-latest) (push) Blocked by required conditions
Validate cache / sbt2-restore (macos-15-intel) (push) Blocked by required conditions
Validate cache / sbt2-restore (ubuntu-22.04) (push) Blocked by required conditions
Validate cache / sbt2-restore (windows-latest) (push) Blocked by required conditions
Validate local file / Validate installation from local file Zulu (macos-latest) (push) Waiting to run
Validate local file / Validate installation from local file Zulu (windows-latest) (push) Waiting to run
Validate local file / Validate installation from local file Eclipse Temurin (macos-latest) (push) Waiting to run
Validate local file / Validate installation from local file Eclipse Temurin (windows-latest) (push) Waiting to run
Validate publishing functionality / Validate settings.xml (macos-latest) (push) Waiting to run
Validate publishing functionality / Validate settings.xml (windows-latest) (push) Waiting to run
Validate publishing functionality / settings.xml is overwritten if flag is true (macos-latest) (push) Waiting to run
Validate publishing functionality / settings.xml is overwritten if flag is true (windows-latest) (push) Waiting to run
Validate publishing functionality / settings.xml is not overwritten if flag is false (macos-latest) (push) Waiting to run
Validate publishing functionality / settings.xml is not overwritten if flag is false (windows-latest) (push) Waiting to run
Validate publishing functionality / settings.xml in custom location (macos-latest) (push) Waiting to run
Validate publishing functionality / settings.xml in custom location (windows-latest) (push) Waiting to run
Validate Java e2e smoke / microsoft 25 (jdk) - macos-latest (push) Waiting to run
Validate Java e2e smoke / microsoft 25 (jdk) - windows-latest (push) Waiting to run
Validate Java e2e smoke / temurin 11 (jdk) - macos-latest (push) Waiting to run
Validate Java e2e smoke / temurin 25 (jdk) - macos-latest (push) Waiting to run
Validate Java e2e smoke / temurin 17 (jdk) - windows-latest (push) Waiting to run
Validate Java e2e smoke / temurin 25 (jdk) - windows-latest (push) Waiting to run
Validate cache / gradle-save (ubuntu-latest) (push) Failing after 5m11s
Validate cache / maven-save (ubuntu-latest) (push) Failing after 36s
Validate cache / sbt-save (ubuntu-22.04) (push) Failing after 35s
Validate cache / gradle1-save (ubuntu-latest) (push) Failing after 4m53s
Validate cache / maven1-save (ubuntu-latest) (push) Failing after 34s
Validate cache / sbt1-save (ubuntu-22.04) (push) Failing after 35s
Validate cache / custom-maven-path-save (push) Failing after 34s
Validate cache / custom-maven-path-restore (push) Skipped
Validate local file / Validate installation from local file Zulu (ubuntu-latest) (push) Failing after 36s
Validate local file / Validate installation from local file Eclipse Temurin (ubuntu-latest) (push) Failing after 36s
Validate publishing functionality / Validate settings.xml (ubuntu-latest) (push) Failing after 36s
Validate publishing functionality / settings.xml is overwritten if flag is true (ubuntu-latest) (push) Failing after 41s
Validate publishing functionality / settings.xml is not overwritten if flag is false (ubuntu-latest) (push) Failing after 36s
Validate publishing functionality / settings.xml in custom location (ubuntu-latest) (push) Failing after 19s
Validate publishing functionality / Maven dependencies repositories (push) Waiting to run
Validate publishing functionality / Maven plugins repositories (push) Waiting to run
Validate publishing functionality / Maven both repositories (push) Waiting to run
Validate Java e2e smoke / liberica 21 (jdk+fx) - ubuntu-latest (push) Failing after 39s
Validate Java e2e smoke / microsoft 25 (jdk) - ubuntu-latest (push) Successful in 1m25s
Validate Java e2e smoke / temurin 21 (jdk) - ubuntu-latest (push) Failing after 32s
Validate Java e2e smoke / temurin 25 (jdk) - ubuntu-latest (push) Failing after 34s
Validate Java e2e smoke / zulu 17 (jre) - ubuntu-latest (push) Successful in 32s
Security analysis with zizmor / Analyze workflows with zizmor (push) Failing after 13m9s
* 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
1430 lines
42 KiB
TypeScript
1430 lines
42 KiB
TypeScript
import {
|
|
jest,
|
|
describe,
|
|
it,
|
|
expect,
|
|
beforeEach,
|
|
afterEach,
|
|
beforeAll,
|
|
afterAll
|
|
} from '@jest/globals';
|
|
import {fileURLToPath} from 'url';
|
|
import * as io from '@actions/io';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import os from 'os';
|
|
import {XMLParser} from 'fast-xml-parser';
|
|
|
|
// Mock @actions/core before importing source modules that depend on it
|
|
jest.unstable_mockModule('@actions/core', () => ({
|
|
info: jest.fn(),
|
|
warning: jest.fn(),
|
|
debug: jest.fn(),
|
|
error: jest.fn(),
|
|
notice: jest.fn(),
|
|
setFailed: jest.fn(),
|
|
setOutput: jest.fn(),
|
|
getInput: jest.fn(),
|
|
getBooleanInput: jest.fn(),
|
|
getMultilineInput: jest.fn(() => []),
|
|
addPath: jest.fn(),
|
|
exportVariable: jest.fn(),
|
|
saveState: jest.fn(),
|
|
getState: jest.fn(),
|
|
setSecret: jest.fn(),
|
|
isDebug: jest.fn(() => false),
|
|
startGroup: jest.fn(),
|
|
endGroup: jest.fn(),
|
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
|
toPlatformPath: jest.fn((p: string) => p),
|
|
toWin32Path: jest.fn((p: string) => p),
|
|
toPosixPath: jest.fn((p: string) => p)
|
|
}));
|
|
|
|
jest.unstable_mockModule('../src/gpg.js', () => ({
|
|
importKey: jest.fn(),
|
|
removeGpgHome: jest.fn(),
|
|
toGpgPath: jest.fn()
|
|
}));
|
|
|
|
// Dynamic imports after mocking
|
|
const core = await import('@actions/core');
|
|
const gpg = await import('../src/gpg.js');
|
|
const auth = await import('../src/auth.js');
|
|
const {M2_DIR, MVN_SETTINGS_FILE, STATE_GPG_HOME} =
|
|
await import('../src/constants.js');
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const m2Dir = path.join(__dirname, M2_DIR);
|
|
const settingsFile = path.join(m2Dir, MVN_SETTINGS_FILE);
|
|
const credentials = (id: string, username: string, password: string) => [
|
|
{id, usernameEnvVar: username, passwordEnvVar: password}
|
|
];
|
|
const repositorySettings = (
|
|
repositories: auth.MavenRepository[],
|
|
includeCentral = 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;
|
|
let spyInfo: any;
|
|
|
|
beforeEach(async () => {
|
|
await io.rmRF(m2Dir);
|
|
spyOSHomedir = jest.spyOn(os, 'homedir');
|
|
spyOSHomedir.mockReturnValue(__dirname);
|
|
spyInfo = core.info as jest.Mock;
|
|
spyInfo.mockImplementation(() => null);
|
|
(gpg.toGpgPath as jest.Mock<any>).mockImplementation((p: string) => p);
|
|
}, 300000);
|
|
|
|
afterEach(() => {
|
|
(core.getInput as jest.Mock).mockReset();
|
|
(core.getMultilineInput as jest.Mock).mockReset();
|
|
(core.getMultilineInput as jest.Mock).mockReturnValue([]);
|
|
(core.warning as jest.Mock).mockReset();
|
|
(core.exportVariable as jest.Mock).mockReset();
|
|
(gpg.importKey as jest.Mock).mockReset();
|
|
(gpg.removeGpgHome as jest.Mock).mockReset();
|
|
(gpg.toGpgPath as jest.Mock).mockReset();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
try {
|
|
await io.rmRF(m2Dir);
|
|
} catch {
|
|
console.log('Failed to remove test directories');
|
|
}
|
|
jest.resetAllMocks();
|
|
jest.clearAllMocks();
|
|
jest.restoreAllMocks();
|
|
}, 100000);
|
|
|
|
it('creates settings.xml in alternate locations', async () => {
|
|
const id = 'packages';
|
|
const username = 'UNAMI';
|
|
const password = 'TOLKIEN';
|
|
|
|
const altHome = path.join(__dirname, 'runner', 'settings');
|
|
const altSettingsFile = path.join(altHome, MVN_SETTINGS_FILE);
|
|
await io.rmRF(altHome); // ensure it doesn't already exist
|
|
|
|
await auth.createAuthenticationSettings(
|
|
credentials(id, username, password),
|
|
altHome,
|
|
true
|
|
);
|
|
|
|
expect(fs.existsSync(m2Dir)).toBe(false);
|
|
expect(fs.existsSync(settingsFile)).toBe(false);
|
|
|
|
expect(fs.existsSync(altHome)).toBe(true);
|
|
expect(fs.existsSync(altSettingsFile)).toBe(true);
|
|
expect(fs.readFileSync(altSettingsFile, 'utf-8')).toEqual(
|
|
auth.generate(credentials(id, username, password))
|
|
);
|
|
|
|
await io.rmRF(altHome);
|
|
}, 100000);
|
|
|
|
it('creates settings.xml with minimal configuration', async () => {
|
|
const id = 'packages';
|
|
const username = 'UNAME';
|
|
const password = 'TOKEN';
|
|
|
|
await auth.createAuthenticationSettings(
|
|
credentials(id, username, password),
|
|
m2Dir,
|
|
true
|
|
);
|
|
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
|
|
auth.generate(credentials(id, username, password))
|
|
);
|
|
}, 100000);
|
|
|
|
it('creates settings.xml with additional configuration', async () => {
|
|
const id = 'packages';
|
|
const username = 'UNAME';
|
|
const password = 'TOKEN';
|
|
const gpgPassphrase = 'GPG';
|
|
|
|
await auth.createAuthenticationSettings(
|
|
credentials(id, username, password),
|
|
m2Dir,
|
|
true,
|
|
gpgPassphrase
|
|
);
|
|
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
|
|
auth.generate(credentials(id, username, password), gpgPassphrase)
|
|
);
|
|
}, 100000);
|
|
|
|
it('exports a GPG-compatible path and persists the native GPG home', async () => {
|
|
const gpgHome = 'D:\\a\\_temp\\setup-java-gpg-1';
|
|
const exportedGpgHome = '/d/a/_temp/setup-java-gpg-1';
|
|
(gpg.importKey as jest.Mock<any>).mockResolvedValue(gpgHome);
|
|
(gpg.toGpgPath as jest.Mock<any>).mockReturnValue(exportedGpgHome);
|
|
(core.getInput as jest.Mock<any>).mockImplementation((name: string) => {
|
|
const inputs: Record<string, string> = {
|
|
'server-id': 'packages',
|
|
'server-username-env-var': 'USERNAME',
|
|
'server-password-env-var': 'PASSWORD',
|
|
'settings-path': m2Dir,
|
|
'gpg-private-key': 'KEY ONE\nKEY TWO'
|
|
};
|
|
return inputs[name] ?? '';
|
|
});
|
|
|
|
await auth.configureAuthentication();
|
|
|
|
expect(gpg.importKey).toHaveBeenCalledWith('KEY ONE\nKEY TWO');
|
|
expect(core.saveState).toHaveBeenCalledWith(STATE_GPG_HOME, gpgHome);
|
|
expect(gpg.toGpgPath).toHaveBeenCalledWith(gpgHome);
|
|
expect(core.exportVariable).toHaveBeenCalledWith(
|
|
'GNUPGHOME',
|
|
exportedGpgHome
|
|
);
|
|
});
|
|
|
|
it('removes the isolated GPG home when environment export fails', async () => {
|
|
const gpgHome = path.join(__dirname, 'runner', 'temp', 'setup-java-gpg-2');
|
|
(gpg.importKey as jest.Mock<any>).mockResolvedValue(gpgHome);
|
|
(core.exportVariable as jest.Mock<any>).mockImplementation(() => {
|
|
throw new Error('environment file unavailable');
|
|
});
|
|
(core.getInput as jest.Mock<any>).mockImplementation((name: string) => {
|
|
if (name === 'gpg-private-key') return 'KEY CONTENTS';
|
|
if (name === 'settings-path') return m2Dir;
|
|
return '';
|
|
});
|
|
|
|
await expect(auth.configureAuthentication()).rejects.toThrow(
|
|
'environment file unavailable'
|
|
);
|
|
|
|
expect(gpg.removeGpgHome).toHaveBeenCalledWith(gpgHome);
|
|
});
|
|
|
|
it('overwrites existing settings.xml files', async () => {
|
|
const id = 'packages';
|
|
const username = 'USERNAME';
|
|
const password = 'PASSWORD';
|
|
|
|
fs.mkdirSync(m2Dir, {recursive: true});
|
|
fs.writeFileSync(settingsFile, 'FAKE FILE');
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
|
|
await auth.createAuthenticationSettings(
|
|
credentials(id, username, password),
|
|
m2Dir,
|
|
true
|
|
);
|
|
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual(
|
|
auth.generate(credentials(id, username, password))
|
|
);
|
|
}, 100000);
|
|
|
|
it('does not overwrite existing settings.xml files', async () => {
|
|
const id = 'packages';
|
|
const username = 'USERNAME';
|
|
const password = 'PASSWORD';
|
|
|
|
fs.mkdirSync(m2Dir, {recursive: true});
|
|
fs.writeFileSync(settingsFile, 'FAKE FILE');
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
|
|
await auth.createAuthenticationSettings(
|
|
credentials(id, username, password),
|
|
m2Dir,
|
|
false
|
|
);
|
|
|
|
expect(fs.existsSync(m2Dir)).toBe(true);
|
|
expect(fs.existsSync(settingsFile)).toBe(true);
|
|
expect(fs.readFileSync(settingsFile, 'utf-8')).toEqual('FAKE FILE');
|
|
}, 100000);
|
|
|
|
it('generates valid settings.xml with minimal configuration', () => {
|
|
const id = 'packages';
|
|
const username = 'USER';
|
|
const password = '&<>"\'\'"><&';
|
|
|
|
const expectedSettings = `<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
|
<interactiveMode>false</interactiveMode>
|
|
<servers>
|
|
<server>
|
|
<id>${id}</id>
|
|
<username>\${env.${username}}</username>
|
|
<password>\${env.&<>"''"><&}</password>
|
|
</server>
|
|
</servers>
|
|
</settings>`;
|
|
|
|
expect(auth.generate(credentials(id, username, password))).toEqual(
|
|
expectedSettings
|
|
);
|
|
});
|
|
|
|
it('generates valid settings.xml with additional configuration', () => {
|
|
const id = 'packages';
|
|
const username = 'USER';
|
|
const password = '&<>"\'\'"><&';
|
|
const gpgPassphrase = 'PASSPHRASE';
|
|
|
|
const expectedSettings = `<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
|
<interactiveMode>false</interactiveMode>
|
|
<servers>
|
|
<server>
|
|
<id>${id}</id>
|
|
<username>\${env.${username}}</username>
|
|
<password>\${env.&<>"''"><&}</password>
|
|
</server>
|
|
</servers>
|
|
<profiles>
|
|
<profile>
|
|
<id>setup-java-gpg</id>
|
|
<properties>
|
|
<gpg.passphraseEnvName>${gpgPassphrase}</gpg.passphraseEnvName>
|
|
</properties>
|
|
</profile>
|
|
</profiles>
|
|
<activeProfiles>
|
|
<activeProfile>setup-java-gpg</activeProfile>
|
|
</activeProfiles>
|
|
</settings>`;
|
|
|
|
expect(
|
|
auth.generate(credentials(id, username, password), gpgPassphrase)
|
|
).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';
|
|
const password = '&<>"\'\'"><&';
|
|
const gpgPassphrase = 'MAVEN_GPG_PASSPHRASE';
|
|
|
|
const expectedSettings = `<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
|
|
<interactiveMode>false</interactiveMode>
|
|
<servers>
|
|
<server>
|
|
<id>${id}</id>
|
|
<username>\${env.${username}}</username>
|
|
<password>\${env.&<>"''"><&}</password>
|
|
</server>
|
|
</servers>
|
|
</settings>`;
|
|
|
|
expect(
|
|
auth.generate(credentials(id, username, password), gpgPassphrase)
|
|
).toEqual(expectedSettings);
|
|
});
|
|
|
|
it('escapes settings.xml values while preserving parsed semantics', () => {
|
|
const id = `packages&<>"'é`;
|
|
const username = `USER&<>"'é`;
|
|
const password = `TOKEN&<>"'é`;
|
|
const gpgPassphrase = `GPG&<>"'é`;
|
|
|
|
const xml = auth.generate(
|
|
credentials(id, username, password),
|
|
gpgPassphrase
|
|
);
|
|
const parsed = parseXmlObject(xml) as any;
|
|
|
|
expect(parsed.settings.interactiveMode).toBe('false');
|
|
expect(xmlElementText(xml, 'id')).toBe(id);
|
|
expect(xmlElementText(xml, 'username')).toBe(`\${env.${username}}`);
|
|
expect(xmlElementText(xml, 'password')).toBe(`\${env.${password}}`);
|
|
expect(xmlElementText(xml, 'gpg.passphraseEnvName')).toBe(gpgPassphrase);
|
|
expect(parsed.settings.activeProfiles.activeProfile).toBe('setup-java-gpg');
|
|
});
|
|
|
|
it('generates ordered settings.xml entries for multiple servers', () => {
|
|
const servers = [
|
|
{
|
|
id: 'releases',
|
|
usernameEnvVar: 'RELEASES_USERNAME',
|
|
passwordEnvVar: 'RELEASES_PASSWORD'
|
|
},
|
|
{
|
|
id: 'snapshots',
|
|
usernameEnvVar: 'SNAPSHOTS_USERNAME',
|
|
passwordEnvVar: 'SNAPSHOTS_PASSWORD'
|
|
}
|
|
];
|
|
|
|
const parsed = parseXmlObject(auth.generate(servers)) as any;
|
|
|
|
expect(parsed.settings.servers.server).toEqual([
|
|
{
|
|
id: 'releases',
|
|
username: '${env.RELEASES_USERNAME}',
|
|
password: '${env.RELEASES_PASSWORD}'
|
|
},
|
|
{
|
|
id: 'snapshots',
|
|
username: '${env.SNAPSHOTS_USERNAME}',
|
|
password: '${env.SNAPSHOTS_PASSWORD}'
|
|
}
|
|
]);
|
|
});
|
|
|
|
it('generates an active profile with Central before custom repositories by default', () => {
|
|
const parsed = parseXmlObject(
|
|
auth.generate(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
undefined,
|
|
repositorySettings([
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com/maven',
|
|
snapshotsEnabled: true
|
|
}
|
|
])
|
|
)
|
|
) as any;
|
|
|
|
expect(parsed.settings.profiles.profile).toEqual({
|
|
id: 'setup-java-repositories',
|
|
repositories: {
|
|
repository: [
|
|
{
|
|
id: 'central',
|
|
url: 'https://repo.maven.apache.org/maven2',
|
|
snapshots: {enabled: 'false'}
|
|
},
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com/maven',
|
|
snapshots: {enabled: 'true'}
|
|
}
|
|
]
|
|
}
|
|
});
|
|
expect(parsed.settings.activeProfiles.activeProfile).toBe(
|
|
'setup-java-repositories'
|
|
);
|
|
});
|
|
|
|
it('places custom repositories before Central when configured', () => {
|
|
const parsed = parseXmlObject(
|
|
auth.generate(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
undefined,
|
|
repositorySettings(
|
|
[
|
|
{
|
|
id: 'first',
|
|
url: 'https://first.example.com',
|
|
snapshotsEnabled: false
|
|
},
|
|
{
|
|
id: 'second',
|
|
url: 'https://second.example.com',
|
|
snapshotsEnabled: true
|
|
}
|
|
],
|
|
true,
|
|
false
|
|
)
|
|
)
|
|
) as any;
|
|
|
|
expect(
|
|
parsed.settings.profiles.profile.repositories.repository.map(
|
|
(repository: {id: string}) => repository.id
|
|
)
|
|
).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(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
undefined,
|
|
repositorySettings(
|
|
[
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com',
|
|
snapshotsEnabled: false
|
|
}
|
|
],
|
|
false
|
|
)
|
|
)
|
|
) as any;
|
|
|
|
expect(parsed.settings.profiles.profile.repositories.repository).toEqual([
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com',
|
|
snapshots: {enabled: 'false'}
|
|
},
|
|
{
|
|
id: 'central',
|
|
url: 'https://repo.maven.apache.org/maven2',
|
|
releases: {enabled: 'false'},
|
|
snapshots: {enabled: 'false'}
|
|
}
|
|
]);
|
|
expect(
|
|
parsed.settings.profiles.profile.pluginRepositories.pluginRepository
|
|
).toEqual(disabledCentralRepositoryXml);
|
|
});
|
|
|
|
it('combines repository and GPG configuration in shared profile blocks', () => {
|
|
const parsed = parseXmlObject(
|
|
auth.generate(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
'GPG_PASSPHRASE',
|
|
repositorySettings(
|
|
[
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com',
|
|
snapshotsEnabled: false
|
|
}
|
|
],
|
|
false
|
|
)
|
|
)
|
|
) as any;
|
|
|
|
expect(parsed.settings.profiles.profile).toEqual([
|
|
{
|
|
id: 'setup-java-repositories',
|
|
repositories: {
|
|
repository: [
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com',
|
|
snapshots: {enabled: 'false'}
|
|
},
|
|
{
|
|
id: 'central',
|
|
url: 'https://repo.maven.apache.org/maven2',
|
|
releases: {enabled: 'false'},
|
|
snapshots: {enabled: 'false'}
|
|
}
|
|
]
|
|
},
|
|
pluginRepositories: {
|
|
pluginRepository: disabledCentralRepositoryXml
|
|
}
|
|
},
|
|
{
|
|
id: 'setup-java-gpg',
|
|
properties: {['gpg.passphraseEnvName']: 'GPG_PASSPHRASE'}
|
|
}
|
|
]);
|
|
expect(parsed.settings.activeProfiles.activeProfile).toEqual([
|
|
'setup-java-repositories',
|
|
'setup-java-gpg'
|
|
]);
|
|
});
|
|
|
|
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&<>');
|
|
expect(xml).toContain('x=1&y=<value>');
|
|
expect(xml).not.toContain('<repositories>');
|
|
});
|
|
|
|
it('escapes repository values while preserving parsed semantics', () => {
|
|
const repository = {
|
|
id: `private&<>"'é`,
|
|
url: `https://repo.example.com/a?x=1&y=<value>"'é`,
|
|
snapshotsEnabled: true
|
|
};
|
|
const xml = auth.generate(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
undefined,
|
|
repositorySettings([repository], false)
|
|
);
|
|
const parsed = parseXmlObject(xml) as any;
|
|
|
|
expect(
|
|
parsed.settings.profiles.profile.repositories.repository.find(
|
|
(entry: {id: string}) => entry.id === repository.id
|
|
)
|
|
).toEqual({
|
|
id: repository.id,
|
|
url: repository.url,
|
|
snapshots: {enabled: 'true'}
|
|
});
|
|
});
|
|
|
|
it('parses and trims multiline Maven server credentials', () => {
|
|
expect(
|
|
auth.parseMavenServerCredentials([
|
|
'',
|
|
' releases : RELEASES_USERNAME : RELEASES_PASSWORD ',
|
|
'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
|
|
])
|
|
).toEqual([
|
|
{
|
|
id: 'releases',
|
|
usernameEnvVar: 'RELEASES_USERNAME',
|
|
passwordEnvVar: 'RELEASES_PASSWORD'
|
|
},
|
|
{
|
|
id: 'snapshots',
|
|
usernameEnvVar: 'SNAPSHOTS_USERNAME',
|
|
passwordEnvVar: 'SNAPSHOTS_PASSWORD'
|
|
}
|
|
]);
|
|
});
|
|
|
|
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(
|
|
[
|
|
{
|
|
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:'],
|
|
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(repositoryInputs)('rejects duplicate IDs in %s', inputName => {
|
|
expect(() =>
|
|
auth.parseMavenRepositories(
|
|
[
|
|
'private:https://first.example.com:false',
|
|
'private:https://second.example.com:true'
|
|
],
|
|
true,
|
|
inputName
|
|
)
|
|
).toThrow(`Duplicate repository-id 'private' in ${inputName} input`);
|
|
});
|
|
|
|
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, 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(
|
|
auth.generate(
|
|
credentials('packages', 'USERNAME', 'PASSWORD'),
|
|
undefined,
|
|
repositorySettings(
|
|
auth.parseMavenRepositories(
|
|
['central:https://mirror.example.com/maven:true'],
|
|
false
|
|
),
|
|
false
|
|
)
|
|
)
|
|
) as any;
|
|
|
|
expect(parsed.settings.profiles.profile.repositories.repository).toEqual({
|
|
id: 'central',
|
|
url: 'https://mirror.example.com/maven',
|
|
snapshots: {enabled: 'true'}
|
|
});
|
|
});
|
|
|
|
it('reads Maven repository settings and Central controls', () => {
|
|
(core.getMultilineInput as jest.Mock).mockImplementation((name: string) =>
|
|
name === 'mvn-repositories'
|
|
? ['private:https://repo.example.com:true']
|
|
: []
|
|
);
|
|
(core.getInput as jest.Mock).mockImplementation((name: string) => {
|
|
const inputs: Record<string, string> = {
|
|
'mvn-repositories-include-central': 'false',
|
|
'mvn-repositories-prioritize-central': 'false'
|
|
};
|
|
return inputs[name] ?? '';
|
|
});
|
|
|
|
expect(auth.getMavenRepositorySettings()).toEqual({
|
|
repositories: [
|
|
{
|
|
id: 'private',
|
|
url: 'https://repo.example.com',
|
|
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'],
|
|
error:
|
|
'Invalid mvn-server-credentials entry at line 1. Expected format: server-id:USERNAME_ENV:PASSWORD_ENV'
|
|
},
|
|
{
|
|
entries: ['', 'releases:RELEASES_USERNAME:RELEASES_PASSWORD:EXTRA'],
|
|
error:
|
|
'Invalid mvn-server-credentials entry at line 2. Expected format: server-id:USERNAME_ENV:PASSWORD_ENV'
|
|
},
|
|
{
|
|
entries: ['releases::RELEASES_PASSWORD'],
|
|
error:
|
|
'Invalid mvn-server-credentials entry at line 1. server-id, username environment variable, and password environment variable are required'
|
|
}
|
|
])(
|
|
'rejects malformed Maven server credentials: $entries',
|
|
({entries, error}) => {
|
|
expect(() => auth.parseMavenServerCredentials(entries)).toThrow(error);
|
|
}
|
|
);
|
|
|
|
it('rejects duplicate Maven server ids', () => {
|
|
expect(() =>
|
|
auth.parseMavenServerCredentials([
|
|
'releases:RELEASES_USERNAME:RELEASES_PASSWORD',
|
|
'releases:OTHER_USERNAME:OTHER_PASSWORD'
|
|
])
|
|
).toThrow("Duplicate server-id 'releases' in mvn-server-credentials input");
|
|
});
|
|
|
|
it('uses multiline Maven server credentials instead of single-server inputs', () => {
|
|
(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',
|
|
repositoryOrigins: ['https://repo.example.com']
|
|
},
|
|
{
|
|
id: 'snapshots',
|
|
usernameEnvVar: 'SNAPSHOTS_USERNAME',
|
|
passwordEnvVar: 'SNAPSHOTS_PASSWORD'
|
|
}
|
|
]);
|
|
expect(core.getInput).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('uses the existing single-server inputs when multiline credentials are absent', () => {
|
|
(core.getInput as jest.Mock).mockImplementation((name: string) =>
|
|
name === 'server-id' ? 'github' : ''
|
|
);
|
|
|
|
expect(auth.getMavenServerSettings()).toEqual([
|
|
{
|
|
id: 'github',
|
|
usernameEnvVar: 'GITHUB_ACTOR',
|
|
passwordEnvVar: 'GITHUB_TOKEN'
|
|
}
|
|
]);
|
|
});
|
|
|
|
it('preserves deprecated single-server aliases as fallback inputs', () => {
|
|
(core.getInput as jest.Mock).mockImplementation((name: string) => {
|
|
const inputs: Record<string, string> = {
|
|
'server-id': 'legacy',
|
|
'server-username': 'LEGACY_USERNAME',
|
|
'server-password': 'LEGACY_PASSWORD'
|
|
};
|
|
return inputs[name] ?? '';
|
|
});
|
|
|
|
expect(auth.getMavenServerSettings()).toEqual([
|
|
{
|
|
id: 'legacy',
|
|
usernameEnvVar: 'LEGACY_USERNAME',
|
|
passwordEnvVar: 'LEGACY_PASSWORD'
|
|
}
|
|
]);
|
|
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();
|
|
return (parseXmlObject(`<value>${match?.[1]}</value>`) as {value: string})
|
|
.value;
|
|
}
|
|
|
|
function parseXmlObject(xml: string): unknown {
|
|
const parser = new XMLParser({
|
|
ignoreAttributes: false,
|
|
attributeNamePrefix: '@',
|
|
parseAttributeValue: false,
|
|
parseTagValue: false,
|
|
trimValues: true
|
|
});
|
|
return parser.parse(xml);
|
|
}
|
|
|
|
it('uses deprecated input aliases and warns', () => {
|
|
const mockGetInput = core.getInput as jest.MockedFunction<
|
|
typeof core.getInput
|
|
>;
|
|
const mockWarning = core.warning as jest.MockedFunction<
|
|
typeof core.warning
|
|
>;
|
|
mockGetInput.mockImplementation(name =>
|
|
name === 'server-username' ? 'LEGACY_USERNAME' : ''
|
|
);
|
|
|
|
expect(
|
|
auth.getInputWithDeprecatedAlias(
|
|
'server-username-env-var',
|
|
'server-username',
|
|
'GITHUB_ACTOR'
|
|
)
|
|
).toBe('LEGACY_USERNAME');
|
|
expect(mockWarning).toHaveBeenCalledWith(
|
|
"The 'server-username' input is deprecated and may be removed in a future release. Please use 'server-username-env-var' instead."
|
|
);
|
|
|
|
mockGetInput.mockReset();
|
|
mockWarning.mockReset();
|
|
});
|
|
|
|
it('prefers the replacement input over its deprecated alias', () => {
|
|
const mockGetInput = core.getInput as jest.MockedFunction<
|
|
typeof core.getInput
|
|
>;
|
|
mockGetInput.mockImplementation(name => {
|
|
const inputs: Record<string, string> = {
|
|
'server-password-env-var': 'NEW_PASSWORD',
|
|
'server-password': 'LEGACY_PASSWORD'
|
|
};
|
|
return inputs[name] || '';
|
|
});
|
|
|
|
expect(
|
|
auth.getInputWithDeprecatedAlias(
|
|
'server-password-env-var',
|
|
'server-password',
|
|
'GITHUB_TOKEN'
|
|
)
|
|
).toBe('NEW_PASSWORD');
|
|
expect(core.warning).toHaveBeenCalled();
|
|
|
|
mockGetInput.mockReset();
|
|
(core.warning as jest.Mock).mockReset();
|
|
});
|
|
});
|