From dff445bec793cfc60434ca6145917e8f8e5aa391 Mon Sep 17 00:00:00 2001 From: Satishchoudhary94 <148007944+Satishchoudhary94@users.noreply.github.com> Date: Sun, 18 Jan 2026 14:08:11 +0000 Subject: [PATCH] fix(#1357): Gracefully handle missing pnpm installation during cache This change prevents the action from failing immediately when pnpm is specified in packageManager but not yet installed (e.g., when using corepack). Changes: - Add isPackageManagerInstalled() function to check if a package manager exists - Update restoreCache to skip caching with a warning if package manager not found - Update cachePackages to skip cache save with a warning if package manager not found - This allows workflows to continue instead of failing - Users can either install pnpm first or disable caching with package-manager-cache: false Fixes #1357 Related: https://github.com/actions/setup-node/issues/1357 --- dist/cache-save/index.js | 29 ++++++++++++++++++++++++++++- dist/setup/index.js | 31 ++++++++++++++++++++++++++++++- src/cache-restore.ts | 15 +++++++++++++++ src/cache-save.ts | 13 ++++++++++++- src/cache-utils.ts | 24 ++++++++++++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 642f19dd5..18b7daa64 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -44084,6 +44084,14 @@ const cachePackages = async (packageManager) => { core.debug(`Caching for '${packageManager}' is not supported`); return; } + // Check if the package manager is installed before attempting to save cache + // This prevents cache save failures for package managers that may not be installed + const isInstalled = await (0, cache_utils_1.isPackageManagerInstalled)(packageManager); + if (!isInstalled) { + core.warning(`Package manager '${packageManager}' was not found in the PATH. ` + + `Skipping cache save.`); + return; + } if (!cachePaths.length) { // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // export declare function getInput(name: string, options?: InputOptions): string; @@ -44147,7 +44155,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.repoHasYarnBerryManagedDependencies = exports.getCacheDirectories = exports.resetProjectDirectoriesMemoized = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; +exports.repoHasYarnBerryManagedDependencies = exports.getCacheDirectories = exports.resetProjectDirectoriesMemoized = exports.isPackageManagerInstalled = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; exports.isGhes = isGhes; exports.isCacheFeatureAvailable = isCacheFeatureAvailable; const core = __importStar(__nccwpck_require__(37484)); @@ -44218,6 +44226,25 @@ const getPackageManagerInfo = async (packageManager) => { } }; exports.getPackageManagerInfo = getPackageManagerInfo; +/** + * Checks if a package manager is installed and available on the PATH + * This helps prevent cache failures when a package manager is specified + * but not yet installed (e.g., pnpm via corepack) + * See: https://github.com/actions/setup-node/issues/1357 + */ +const isPackageManagerInstalled = async (packageManager) => { + try { + const { exitCode } = await exec.getExecOutput(`${packageManager} --version`, undefined, { + ignoreReturnCode: true, + silent: true + }); + return exitCode === 0; + } + catch { + return false; + } +}; +exports.isPackageManagerInstalled = isPackageManagerInstalled; /** * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache` * - first through `getCacheDirectories` diff --git a/dist/setup/index.js b/dist/setup/index.js index c59bedb4f..8ca083935 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -53696,6 +53696,16 @@ const restoreCache = async (packageManager, cacheDependencyPath) => { if (!packageManagerInfo) { throw new Error(`Caching for '${packageManager}' is not supported`); } + // Check if the package manager is installed before attempting to cache + // This prevents cache failures for package managers that need to be installed first + // See: https://github.com/actions/setup-node/issues/1357 + const isInstalled = await (0, cache_utils_1.isPackageManagerInstalled)(packageManager); + if (!isInstalled) { + core.warning(`Package manager '${packageManager}' was not found in the PATH. ` + + `Skipping cache restore. Please ensure the package manager is installed ` + + `before running this action or set 'package-manager-cache: false' to disable caching.`); + return; + } const platform = process.env.RUNNER_OS; const arch = os_1.default.arch(); const cachePaths = await (0, cache_utils_1.getCacheDirectories)(packageManagerInfo, cacheDependencyPath); @@ -53785,7 +53795,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.repoHasYarnBerryManagedDependencies = exports.getCacheDirectories = exports.resetProjectDirectoriesMemoized = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; +exports.repoHasYarnBerryManagedDependencies = exports.getCacheDirectories = exports.resetProjectDirectoriesMemoized = exports.isPackageManagerInstalled = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; exports.isGhes = isGhes; exports.isCacheFeatureAvailable = isCacheFeatureAvailable; const core = __importStar(__nccwpck_require__(37484)); @@ -53856,6 +53866,25 @@ const getPackageManagerInfo = async (packageManager) => { } }; exports.getPackageManagerInfo = getPackageManagerInfo; +/** + * Checks if a package manager is installed and available on the PATH + * This helps prevent cache failures when a package manager is specified + * but not yet installed (e.g., pnpm via corepack) + * See: https://github.com/actions/setup-node/issues/1357 + */ +const isPackageManagerInstalled = async (packageManager) => { + try { + const { exitCode } = await exec.getExecOutput(`${packageManager} --version`, undefined, { + ignoreReturnCode: true, + silent: true + }); + return exitCode === 0; + } + catch { + return false; + } +}; +exports.isPackageManagerInstalled = isPackageManagerInstalled; /** * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache` * - first through `getCacheDirectories` diff --git a/src/cache-restore.ts b/src/cache-restore.ts index af12ad83c..bc06b9e06 100644 --- a/src/cache-restore.ts +++ b/src/cache-restore.ts @@ -9,6 +9,7 @@ import {State} from './constants'; import { getCacheDirectories, getPackageManagerInfo, + isPackageManagerInstalled, repoHasYarnBerryManagedDependencies, PackageManagerInfo } from './cache-utils'; @@ -21,6 +22,20 @@ export const restoreCache = async ( if (!packageManagerInfo) { throw new Error(`Caching for '${packageManager}' is not supported`); } + + // Check if the package manager is installed before attempting to cache + // This prevents cache failures for package managers that need to be installed first + // See: https://github.com/actions/setup-node/issues/1357 + const isInstalled = await isPackageManagerInstalled(packageManager); + if (!isInstalled) { + core.warning( + `Package manager '${packageManager}' was not found in the PATH. ` + + `Skipping cache restore. Please ensure the package manager is installed ` + + `before running this action or set 'package-manager-cache: false' to disable caching.` + ); + return; + } + const platform = process.env.RUNNER_OS; const arch = os.arch(); diff --git a/src/cache-save.ts b/src/cache-save.ts index bbfd2de68..de23084cb 100644 --- a/src/cache-save.ts +++ b/src/cache-save.ts @@ -2,7 +2,7 @@ import * as core from '@actions/core'; import * as cache from '@actions/cache'; import {State} from './constants'; -import {getPackageManagerInfo} from './cache-utils'; +import {getPackageManagerInfo, isPackageManagerInstalled} from './cache-utils'; // Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in // @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to @@ -45,6 +45,17 @@ const cachePackages = async (packageManager: string) => { return; } + // Check if the package manager is installed before attempting to save cache + // This prevents cache save failures for package managers that may not be installed + const isInstalled = await isPackageManagerInstalled(packageManager); + if (!isInstalled) { + core.warning( + `Package manager '${packageManager}' was not found in the PATH. ` + + `Skipping cache save.` + ); + return; + } + if (!cachePaths.length) { // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // export declare function getInput(name: string, options?: InputOptions): string; diff --git a/src/cache-utils.ts b/src/cache-utils.ts index 6f45749a7..4e345ed5a 100644 --- a/src/cache-utils.ts +++ b/src/cache-utils.ts @@ -110,6 +110,30 @@ export const getPackageManagerInfo = async (packageManager: string) => { } }; +/** + * Checks if a package manager is installed and available on the PATH + * This helps prevent cache failures when a package manager is specified + * but not yet installed (e.g., pnpm via corepack) + * See: https://github.com/actions/setup-node/issues/1357 + */ +export const isPackageManagerInstalled = async ( + packageManager: string +): Promise => { + try { + const {exitCode} = await exec.getExecOutput( + `${packageManager} --version`, + undefined, + { + ignoreReturnCode: true, + silent: true + } + ); + return exitCode === 0; + } catch { + return false; + } +}; + /** * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache` * - first through `getCacheDirectories`