diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 7e3a1e56..690f4a6a 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -68,7 +68,7 @@ jest.unstable_mockModule('@actions/glob', () => ({ const core = await import('@actions/core'); const cache = await import('@actions/cache'); const glob = await import('@actions/glob'); -const {restore, save} = await import('../src/cache.js'); +const {restore, save, validatePackageManager} = await import('../src/cache.js'); describe('dependency cache', () => { const ORIGINAL_RUNNER_OS = process.env['RUNNER_OS']; @@ -131,6 +131,20 @@ describe('dependency cache', () => { jest.restoreAllMocks(); }); + describe('validatePackageManager', () => { + it('accepts supported package managers', () => { + expect(() => validatePackageManager('maven')).not.toThrow(); + expect(() => validatePackageManager('gradle')).not.toThrow(); + expect(() => validatePackageManager('sbt')).not.toThrow(); + }); + + it('throws the targeted error for unsupported package managers', () => { + expect(() => validatePackageManager('ant')).toThrow( + 'unknown package manager specified: ant' + ); + }); + }); + describe('restore', () => { let spyCacheRestore: any; let spyGlobHashFiles: any; diff --git a/__tests__/setup-java.test.ts b/__tests__/setup-java.test.ts index a693d3f4..6453c6c1 100644 --- a/__tests__/setup-java.test.ts +++ b/__tests__/setup-java.test.ts @@ -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(); + const cacheRestoreCalled = deferred(); + (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() { diff --git a/dist/cleanup/377.index.js b/dist/cleanup/377.index.js index b6de5595..66c5d2a5 100644 --- a/dist/cleanup/377.index.js +++ b/dist/cleanup/377.index.js @@ -8,7 +8,7 @@ export const modules = { /* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ save: () => (/* binding */ save) /* harmony export */ }); -/* unused harmony export restore */ +/* unused harmony exports validatePackageManager, restore */ /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6928); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var os__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(857); @@ -108,6 +108,9 @@ function findPackageManager(id) { } return packageManager; } +function validatePackageManager(id) { + findPackageManager(id); +} function resolveCachePaths(packageManager, cachePaths) { return cachePaths.length > 0 ? cachePaths : packageManager.path; } diff --git a/dist/setup/377.index.js b/dist/setup/377.index.js index 743d977d..e5439ef1 100644 --- a/dist/setup/377.index.js +++ b/dist/setup/377.index.js @@ -6,7 +6,8 @@ export const modules = { /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { /* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ restore: () => (/* binding */ restore) +/* harmony export */ restore: () => (/* binding */ restore), +/* harmony export */ validatePackageManager: () => (/* binding */ validatePackageManager) /* harmony export */ }); /* unused harmony export save */ /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6928); @@ -108,6 +109,9 @@ function findPackageManager(id) { } return packageManager; } +function validatePackageManager(id) { + findPackageManager(id); +} function resolveCachePaths(packageManager, cachePaths) { return cachePaths.length > 0 ? cachePaths : packageManager.path; } diff --git a/dist/setup/index.js b/dist/setup/index.js index 06d35615..1d557e4a 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -36380,8 +36380,9 @@ async function run() { jdkFile, toolchainIds }; + await validateCacheInput(cache); cacheRestore = cache - ? startCacheRestore(cache, cacheDependencyPath, cachePath) + ? settle(startCacheRestore(cache, cacheDependencyPath, cachePath)) : undefined; toolchainConfigurations.push(await installVersion(versionInfo.version, installerInputsOptions)); } @@ -36403,8 +36404,9 @@ async function run() { jdkFile, toolchainIds }; + await validateCacheInput(cache); cacheRestore = cache - ? startCacheRestore(cache, cacheDependencyPath, cachePath) + ? settle(startCacheRestore(cache, cacheDependencyPath, cachePath)) : undefined; for (const [index, version] of versions.entries()) { toolchainConfigurations.push(await installVersion(version, installerInputsOptions, index)); @@ -36419,19 +36421,25 @@ async function run() { actionError = error; } if (cacheRestore) { - try { - await cacheRestore; - } - catch (error) { - if (!actionError) { - actionError = error; - } + const cacheResult = await cacheRestore; + if (cacheResult.status === 'rejected' && !actionError) { + actionError = cacheResult.reason; } } if (actionError) { setup_java_core/* setFailed */.C1(actionError.message); } } +async function validateCacheInput(cache) { + if (!cache) { + return; + } + const { validatePackageManager } = await Promise.all(/* import() */[__nccwpck_require__.e(824), __nccwpck_require__.e(971), __nccwpck_require__.e(377)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7377)); + validatePackageManager(cache); +} +function settle(promise) { + return promise.then(value => ({ status: 'fulfilled', value }), reason => ({ status: 'rejected', reason })); +} if (process.argv[1] === (0,external_url_.fileURLToPath)(import.meta.url)) { run(); } diff --git a/src/cache.ts b/src/cache.ts index a73fb44a..f16bca8d 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -138,6 +138,10 @@ function findPackageManager(id: string): PackageManager { return packageManager; } +export function validatePackageManager(id: string): void { + findPackageManager(id); +} + function resolveCachePaths( packageManager: PackageManager, cachePaths: string[] diff --git a/src/setup-java.ts b/src/setup-java.ts index c361c5ff..37759957 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -37,7 +37,7 @@ export async function run() { core.getInput(constants.INPUT_VERIFY_SIGNATURE_PUBLIC_KEY) || undefined; const toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); let actionError: Error | undefined; - let cacheRestore: Promise | undefined; + let cacheRestore: Promise> | undefined; const toolchainConfigurations: ToolchainConfiguration[] = []; try { @@ -94,8 +94,9 @@ export async function run() { toolchainIds }; + await validateCacheInput(cache); cacheRestore = cache - ? startCacheRestore(cache, cacheDependencyPath, cachePath) + ? settle(startCacheRestore(cache, cacheDependencyPath, cachePath)) : undefined; toolchainConfigurations.push( await installVersion(versionInfo.version, installerInputsOptions) @@ -120,8 +121,9 @@ export async function run() { toolchainIds }; + await validateCacheInput(cache); cacheRestore = cache - ? startCacheRestore(cache, cacheDependencyPath, cachePath) + ? settle(startCacheRestore(cache, cacheDependencyPath, cachePath)) : undefined; for (const [index, version] of versions.entries()) { toolchainConfigurations.push( @@ -144,12 +146,9 @@ export async function run() { } if (cacheRestore) { - try { - await cacheRestore; - } catch (error) { - if (!actionError) { - actionError = error as Error; - } + const cacheResult = await cacheRestore; + if (cacheResult.status === 'rejected' && !actionError) { + actionError = cacheResult.reason as Error; } } @@ -158,6 +157,21 @@ export async function run() { } } +async function validateCacheInput(cache: string): Promise { + if (!cache) { + return; + } + const {validatePackageManager} = await import('./cache.js'); + validatePackageManager(cache); +} + +function settle(promise: Promise): Promise> { + return promise.then, PromiseRejectedResult>( + value => ({status: 'fulfilled', value}), + reason => ({status: 'rejected', reason}) + ); +} + if (process.argv[1] === fileURLToPath(import.meta.url)) { run(); } else {