Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ steps:
| `server-password-env-var` | Environment variable name for Maven repository password or token. | `GITHUB_TOKEN` |
| `settings-path` | Directory where `settings.xml` is written. | `~/.m2` |
| `overwrite-settings` | Overwrite an existing `settings.xml`. | `true` |
| `gpg-private-key` | GPG private key to import. | |
| `gpg-private-key` | GPG private key to import into an isolated temporary keyring. | |
| `gpg-passphrase-env-var` | Environment variable name for the GPG private key passphrase. | `GPG_PASSPHRASE` when a key is set |
| `mvn-toolchain-id` | Maven Toolchain ID. When multiple Java versions are installed, the number of IDs must match the number of versions. | `${mvn-toolchain-vendor}_${java-version}` |
| `mvn-toolchain-vendor` | Maven Toolchain vendor value. | `${distribution}` |
Expand Down
65 changes: 64 additions & 1 deletion __tests__/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,18 @@ jest.unstable_mockModule('@actions/core', () => ({
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} = await import('../src/constants.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);
Expand All @@ -60,8 +68,17 @@ describe('auth tests', () => {
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.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);
Expand Down Expand Up @@ -144,6 +161,52 @@ describe('auth tests', () => {
);
}, 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';
Expand Down
59 changes: 58 additions & 1 deletion __tests__/cleanup-java.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const core = await import('@actions/core');
const cache = await import('@actions/cache');
const {run: cleanup} = await import('../src/cleanup-java.js');
const util = await import('../src/util.js');
const constants = await import('../src/constants.js');
const {GPG_HOME_PREFIX} = await import('../src/gpg.js');
const {registerJdk, buildJdkCacheKey} = await import('../src/jdk-cache.js');

const jdkTempRoots: string[] = [];
Expand Down Expand Up @@ -114,11 +116,65 @@ describe('cleanup', () => {
(core.getInput as jest.Mock<any>).mockImplementation((name: string) => {
return name === 'cache' ? 'gradle' : '';
});

await cleanup();
expect(spyCacheSave).toHaveBeenCalled();
expect(spyWarning).not.toHaveBeenCalled();
});

it('removes the isolated GPG home without touching unrelated key material', async () => {
const tempDir = util.getTempDir();
fs.mkdirSync(tempDir, {recursive: true});
const gpgHome = fs.mkdtempSync(path.join(tempDir, GPG_HOME_PREFIX));
const unrelatedGpgHome = fs.mkdtempSync(
path.join(tempDir, 'user-gpg-home-')
);
fs.writeFileSync(
path.join(unrelatedGpgHome, 'private.key'),
'pre-existing'
);
(core.getInput as jest.Mock<any>).mockReturnValue('');
(core.getState as jest.Mock<any>).mockImplementation((name: string) =>
name === constants.STATE_GPG_HOME ? gpgHome : ''
);

await cleanup();

expect(fs.existsSync(gpgHome)).toBe(false);
expect(
fs.readFileSync(path.join(unrelatedGpgHome, 'private.key'), 'utf8')
).toBe('pre-existing');
fs.rmSync(unrelatedGpgHome, {recursive: true, force: true});
});

it('makes repeated cleanup of the same GPG home idempotent', async () => {
const tempDir = util.getTempDir();
fs.mkdirSync(tempDir, {recursive: true});
const gpgHome = fs.mkdtempSync(path.join(tempDir, GPG_HOME_PREFIX));
(core.getInput as jest.Mock<any>).mockReturnValue('');
(core.getState as jest.Mock<any>).mockImplementation((name: string) =>
name === constants.STATE_GPG_HOME ? gpgHome : ''
);

await cleanup();
await cleanup();

expect(fs.existsSync(gpgHome)).toBe(false);
expect(core.setFailed).not.toHaveBeenCalled();
});

it('skips GPG cleanup when no home was persisted', async () => {
(core.getInput as jest.Mock<any>).mockReturnValue('');
(core.getState as jest.Mock<any>).mockReturnValue('');

await cleanup();

expect(spyInfo).not.toHaveBeenCalledWith(
'Removing private key from isolated GPG home'
);
expect(core.setFailed).not.toHaveBeenCalled();
});

it('does not fail even though the save process throws error', async () => {
spyCacheSave.mockImplementation((paths: string[], key: string) =>
Promise.reject(new Error('Unexpected error'))
Expand Down Expand Up @@ -148,7 +204,8 @@ describe('cleanup', () => {
await cleanup();

expect(spyCacheSave).not.toHaveBeenCalled();
expect(core.getState).not.toHaveBeenCalled();
expect(core.getState).toHaveBeenCalledTimes(1);
expect(core.getState).toHaveBeenCalledWith(constants.STATE_GPG_HOME);
expect(spyInfo).toHaveBeenCalledWith(
'Cache saving is skipped because cache-read-only is enabled.'
);
Expand Down
2 changes: 1 addition & 1 deletion __tests__/distributors/microsoft-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jest.unstable_mockModule('../../src/util.js', () => ({

jest.unstable_mockModule('../../src/gpg.js', () => ({
importKey: jest.fn(),
deleteKey: jest.fn(),
removeGpgHome: jest.fn(),
verifyPackageSignature: jest.fn()
}));

Expand Down
2 changes: 1 addition & 1 deletion __tests__/distributors/temurin-installer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jest.unstable_mockModule('../../src/util.js', () => ({

jest.unstable_mockModule('../../src/gpg.js', () => ({
importKey: jest.fn(),
deleteKey: jest.fn(),
removeGpgHome: jest.fn(),
verifyPackageSignature: jest.fn()
}));

Expand Down
Loading
Loading