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
This commit is contained in:
Satishchoudhary94 2026-01-18 14:08:11 +00:00
parent 6044e13b5d
commit dff445bec7
5 changed files with 109 additions and 3 deletions

View File

@ -44084,6 +44084,14 @@ const cachePackages = async (packageManager) => {
core.debug(`Caching for '${packageManager}' is not supported`); core.debug(`Caching for '${packageManager}' is not supported`);
return; 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) { if (!cachePaths.length) {
// TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?)
// export declare function getInput(name: string, options?: InputOptions): string; // 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 }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); 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.isGhes = isGhes;
exports.isCacheFeatureAvailable = isCacheFeatureAvailable; exports.isCacheFeatureAvailable = isCacheFeatureAvailable;
const core = __importStar(__nccwpck_require__(37484)); const core = __importStar(__nccwpck_require__(37484));
@ -44218,6 +44226,25 @@ const getPackageManagerInfo = async (packageManager) => {
} }
}; };
exports.getPackageManagerInfo = getPackageManagerInfo; 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` * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache`
* - first through `getCacheDirectories` * - first through `getCacheDirectories`

31
dist/setup/index.js vendored
View File

@ -53696,6 +53696,16 @@ const restoreCache = async (packageManager, cacheDependencyPath) => {
if (!packageManagerInfo) { if (!packageManagerInfo) {
throw new Error(`Caching for '${packageManager}' is not supported`); 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 platform = process.env.RUNNER_OS;
const arch = os_1.default.arch(); const arch = os_1.default.arch();
const cachePaths = await (0, cache_utils_1.getCacheDirectories)(packageManagerInfo, cacheDependencyPath); 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 }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); 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.isGhes = isGhes;
exports.isCacheFeatureAvailable = isCacheFeatureAvailable; exports.isCacheFeatureAvailable = isCacheFeatureAvailable;
const core = __importStar(__nccwpck_require__(37484)); const core = __importStar(__nccwpck_require__(37484));
@ -53856,6 +53866,25 @@ const getPackageManagerInfo = async (packageManager) => {
} }
}; };
exports.getPackageManagerInfo = getPackageManagerInfo; 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` * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache`
* - first through `getCacheDirectories` * - first through `getCacheDirectories`

View File

@ -9,6 +9,7 @@ import {State} from './constants';
import { import {
getCacheDirectories, getCacheDirectories,
getPackageManagerInfo, getPackageManagerInfo,
isPackageManagerInstalled,
repoHasYarnBerryManagedDependencies, repoHasYarnBerryManagedDependencies,
PackageManagerInfo PackageManagerInfo
} from './cache-utils'; } from './cache-utils';
@ -21,6 +22,20 @@ export const restoreCache = async (
if (!packageManagerInfo) { if (!packageManagerInfo) {
throw new Error(`Caching for '${packageManager}' is not supported`); 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 platform = process.env.RUNNER_OS;
const arch = os.arch(); const arch = os.arch();

View File

@ -2,7 +2,7 @@ import * as core from '@actions/core';
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import {State} from './constants'; 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 // 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 // @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; 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) { if (!cachePaths.length) {
// TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?)
// export declare function getInput(name: string, options?: InputOptions): string; // export declare function getInput(name: string, options?: InputOptions): string;

View File

@ -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<boolean> => {
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` * getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache`
* - first through `getCacheDirectories` * - first through `getCacheDirectories`