mirror of
https://github.com/actions/setup-java.git
synced 2026-08-10 16:39:28 +08:00
Handle early dependency cache failures during Java setup (#1226)
* Handle cache restore promise rejections Validate dependency cache providers before Java installation and settle cache restore failures immediately while preserving setup error precedence. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Make cache-rejection regression test deterministic Reject the cache restore only after it has started and while the Java installation is still pending, instead of relying on event-loop timing. Verified the test fails against the pre-fix implementation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Validate cache input after required input checks Move the package-manager validation out of the top of run() so that missing java-version/java-version-file and toolchain id errors keep their original precedence. Validation now happens immediately before the cache restore is started, which still fails fast before any JDK download. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges <brborges@microsoft.com>
This commit is contained in:
@@ -47,7 +47,8 @@ jest.unstable_mockModule('../src/toolchain-ids.js', () => ({
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('../src/cache.js', () => ({
|
||||
restore: jest.fn()
|
||||
restore: jest.fn(),
|
||||
validatePackageManager: jest.fn()
|
||||
}));
|
||||
|
||||
jest.unstable_mockModule('../src/cache-feature.js', () => ({
|
||||
@@ -129,6 +130,9 @@ describe('setup action orchestration', () => {
|
||||
(toolchains.configureToolchains as jest.Mock).mockResolvedValue(undefined);
|
||||
(auth.configureAuthentication as jest.Mock).mockResolvedValue(undefined);
|
||||
(cache.restore as jest.Mock).mockResolvedValue(undefined);
|
||||
(cache.validatePackageManager as jest.Mock).mockImplementation(
|
||||
() => undefined
|
||||
);
|
||||
});
|
||||
|
||||
it('does not execute the action when imported', () => {
|
||||
@@ -486,6 +490,41 @@ describe('setup action orchestration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('fails invalid cache input before resolving a Java distribution', async () => {
|
||||
inputs.set('distribution', 'temurin');
|
||||
inputs.set('cache', 'ant');
|
||||
multilineInputs.set('java-version', ['21']);
|
||||
const setupJava = jest.fn();
|
||||
(factory.getJavaDistribution as jest.Mock).mockReturnValue({setupJava});
|
||||
(cache.validatePackageManager as jest.Mock).mockImplementation(() => {
|
||||
throw new Error('unknown package manager specified: ant');
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
'unknown package manager specified: ant'
|
||||
);
|
||||
expect(factory.getJavaDistribution).not.toHaveBeenCalled();
|
||||
expect(setupJava).not.toHaveBeenCalled();
|
||||
expect(cache.restore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reports missing java-version before validating the cache input', async () => {
|
||||
inputs.set('distribution', 'temurin');
|
||||
inputs.set('cache', 'ant');
|
||||
(cache.validatePackageManager as jest.Mock).mockImplementation(() => {
|
||||
throw new Error('unknown package manager specified: ant');
|
||||
});
|
||||
|
||||
await run();
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
'java-version or java-version-file input expected'
|
||||
);
|
||||
expect(cache.validatePackageManager).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['', '', false],
|
||||
['', 'true', true],
|
||||
@@ -592,6 +631,47 @@ describe('setup action orchestration', () => {
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith('download failed');
|
||||
});
|
||||
|
||||
it('observes cache failures while Java setup is still pending', async () => {
|
||||
inputs.set('distribution', 'temurin');
|
||||
inputs.set('cache', 'maven');
|
||||
multilineInputs.set('java-version', ['21']);
|
||||
const javaSetup = deferred<{version: string; path: string}>();
|
||||
const setupJava = jest.fn(() => javaSetup.promise);
|
||||
(factory.getJavaDistribution as jest.Mock).mockReturnValue({setupJava});
|
||||
const cacheRestore = deferred<void>();
|
||||
const cacheRestoreCalled = deferred<void>();
|
||||
(cache.restore as jest.Mock).mockImplementation(() => {
|
||||
cacheRestoreCalled.resolve();
|
||||
return cacheRestore.promise;
|
||||
});
|
||||
const unhandledRejections: unknown[] = [];
|
||||
const onUnhandledRejection = (reason: unknown) => {
|
||||
unhandledRejections.push(reason);
|
||||
};
|
||||
process.on('unhandledRejection', onUnhandledRejection);
|
||||
|
||||
const runPromise = run();
|
||||
try {
|
||||
// Only reject once the restore has actually started and while the Java
|
||||
// installation is still pending, so the unfixed code would leave the
|
||||
// rejection unhandled.
|
||||
await cacheRestoreCalled.promise;
|
||||
cacheRestore.reject(new Error('cache restore failed'));
|
||||
await tick();
|
||||
|
||||
expect(setupJava).toHaveBeenCalled();
|
||||
expect(unhandledRejections).toEqual([]);
|
||||
expect(core.setFailed).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
javaSetup.resolve({version: '21.0.4+7', path: '/opt/java/21'});
|
||||
await runPromise;
|
||||
process.off('unhandledRejection', onUnhandledRejection);
|
||||
}
|
||||
|
||||
expect(unhandledRejections).toEqual([]);
|
||||
expect(core.setFailed).toHaveBeenCalledWith('cache restore failed');
|
||||
});
|
||||
});
|
||||
|
||||
function deferred<T>() {
|
||||
|
||||
Reference in New Issue
Block a user