From 9ddc512bc1770f4522d2a1614b4de57894596f4c Mon Sep 17 00:00:00 2001 From: Sergey Dolin Date: Tue, 30 May 2023 14:15:01 +0200 Subject: [PATCH] Apply fixes --- __tests__/cache-restore.test.ts | 8 +- __tests__/cache-save.test.ts | 28 ++--- __tests__/cache-utils.test.ts | 76 ++++--------- dist/cache-save/index.js | 143 +++++++++---------------- dist/setup/index.js | 143 +++++++++---------------- src/cache-restore.ts | 4 +- src/cache-save.ts | 4 +- src/cache-utils.ts | 184 +++++++++++--------------------- 8 files changed, 193 insertions(+), 397 deletions(-) diff --git a/__tests__/cache-restore.test.ts b/__tests__/cache-restore.test.ts index ba20a22d..86dff3f3 100644 --- a/__tests__/cache-restore.test.ts +++ b/__tests__/cache-restore.test.ts @@ -32,13 +32,13 @@ describe('cache-restore', () => { function findCacheFolder(command: string) { switch (command) { - case utils.npmGetCacheFolderCommand: + case 'npm config get cache': return npmCachePath; - case utils.pnpmGetCacheFolderCommand: + case 'pnpm store path --silent': return pnpmCachePath; - case utils.yarn1GetCacheFolderCommand: + case 'yarn cache dir': return yarn1CachePath; - case utils.yarn2GetCacheFolderCommand: + case 'yarn config get cacheFolder': return yarn2CachePath; default: return 'packge/not/found'; diff --git a/__tests__/cache-save.test.ts b/__tests__/cache-save.test.ts index 4fc99792..c84621c5 100644 --- a/__tests__/cache-save.test.ts +++ b/__tests__/cache-save.test.ts @@ -118,13 +118,10 @@ describe('run', () => { expect(getStateSpy).toHaveBeenCalledTimes(2); expect(getCommandOutputSpy).toHaveBeenCalledTimes(2); expect(debugSpy).toHaveBeenCalledWith( - 'Project directory "." derived from cache-dependency-path: ""' + 'Consumed yarn version is 1.2.3 (working dir: "")' ); expect(debugSpy).toHaveBeenCalledWith( - 'Consumed yarn version is 1.2.3 (working dir: ".")' - ); - expect(debugSpy).toHaveBeenCalledWith( - 'yarn\'s cache folder "/some/random/path/yarn1" configured for the directory "."' + 'yarn\'s cache folder "/some/random/path/yarn1" configured for the root directory' ); expect(infoSpy).toHaveBeenCalledWith( `Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.` @@ -145,13 +142,10 @@ describe('run', () => { expect(getStateSpy).toHaveBeenCalledTimes(2); expect(getCommandOutputSpy).toHaveBeenCalledTimes(2); expect(debugSpy).toHaveBeenCalledWith( - 'Project directory "." derived from cache-dependency-path: ""' + 'Consumed yarn version is 2.2.3 (working dir: "")' ); expect(debugSpy).toHaveBeenCalledWith( - 'Consumed yarn version is 2.2.3 (working dir: ".")' - ); - expect(debugSpy).toHaveBeenCalledWith( - 'yarn\'s cache folder "/some/random/path/yarn2" configured for the directory "."' + 'yarn\'s cache folder "/some/random/path/yarn2" configured for the root directory' ); expect(infoSpy).toHaveBeenCalledWith( `Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.` @@ -218,13 +212,10 @@ describe('run', () => { expect(getStateSpy).toHaveBeenCalledTimes(2); expect(getCommandOutputSpy).toHaveBeenCalledTimes(2); expect(debugSpy).toHaveBeenCalledWith( - 'Project directory "." derived from cache-dependency-path: ""' + 'Consumed yarn version is 1.2.3 (working dir: "")' ); expect(debugSpy).toHaveBeenCalledWith( - 'Consumed yarn version is 1.2.3 (working dir: ".")' - ); - expect(debugSpy).toHaveBeenCalledWith( - 'yarn\'s cache folder "/some/random/path/yarn1" configured for the directory "."' + 'yarn\'s cache folder "/some/random/path/yarn1" configured for the root directory' ); expect(infoSpy).not.toHaveBeenCalledWith( `Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.` @@ -255,13 +246,10 @@ describe('run', () => { expect(getStateSpy).toHaveBeenCalledTimes(2); expect(getCommandOutputSpy).toHaveBeenCalledTimes(2); expect(debugSpy).toHaveBeenCalledWith( - 'Project directory "." derived from cache-dependency-path: ""' + 'Consumed yarn version is 2.2.3 (working dir: "")' ); expect(debugSpy).toHaveBeenCalledWith( - 'Consumed yarn version is 2.2.3 (working dir: ".")' - ); - expect(debugSpy).toHaveBeenCalledWith( - 'yarn\'s cache folder "/some/random/path/yarn2" configured for the directory "."' + 'yarn\'s cache folder "/some/random/path/yarn2" configured for the root directory' ); expect(infoSpy).not.toHaveBeenCalledWith( `Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.` diff --git a/__tests__/cache-utils.test.ts b/__tests__/cache-utils.test.ts index 789d3ba6..da9c793b 100644 --- a/__tests__/cache-utils.test.ts +++ b/__tests__/cache-utils.test.ts @@ -7,8 +7,7 @@ import { isCacheFeatureAvailable, supportedPackageManagers, getCommandOutput, - expandCacheDependencyPath, - expandedPatternsMemoized + memoizedCacheDependencies } from '../src/cache-utils'; import fs from 'fs'; import * as cacheUtils from '../src/cache-utils'; @@ -106,8 +105,8 @@ describe('cache-utils', () => { MockGlobber.create(['/foo', '/bar']) ); - Object.keys(expandedPatternsMemoized).forEach( - key => delete expandedPatternsMemoized[key] + Object.keys(memoizedCacheDependencies).forEach( + key => delete memoizedCacheDependencies[key] ); }); @@ -117,44 +116,6 @@ describe('cache-utils', () => { globCreateSpy.mockRestore(); }); - it('expandCacheDependencyPath should handle one line', async () => { - expect(await expandCacheDependencyPath('one')).toEqual(['one']); - }); - - it('expandCacheDependencyPath should handle one line glob', async () => { - globCreateSpy.mockImplementation( - (pattern: string): Promise => - MockGlobber.create(['one', 'two']) - ); - expect(await expandCacheDependencyPath('**')).toEqual(['one', 'two']); - }); - - it('expandCacheDependencyPath should handle multiple lines', async () => { - const lines = ` - one -two - - `; - expect(await expandCacheDependencyPath(lines)).toEqual(['one', 'two']); - }); - - it('expandCacheDependencyPath should handle multiple globs', async () => { - const lines = ` - one -** - - `; - globCreateSpy.mockImplementation( - (pattern: string): Promise => - MockGlobber.create(['two', 'three']) - ); - expect(await expandCacheDependencyPath(lines)).toEqual([ - 'one', - 'two', - 'three' - ]); - }); - it.each([ [supportedPackageManagers.npm, ''], [supportedPackageManagers.npm, '/dir/file.lock'], @@ -167,7 +128,7 @@ two async (packageManagerInfo, cacheDependency) => { getCommandOutputSpy.mockImplementation(() => 'foo'); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( packageManagerInfo, cacheDependency ); @@ -181,7 +142,7 @@ two it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => { getCommandOutputSpy.mockImplementation(() => 'foo'); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, '' ); @@ -208,10 +169,7 @@ two ); await expect( - cacheUtils.getCacheDirectoriesPaths( - packageManagerInfo, - cacheDependency - ) + cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency) ).rejects.toThrow(); //'Could not get cache folder path for /dir'); } ); @@ -234,10 +192,7 @@ two })); await expect( - cacheUtils.getCacheDirectoriesPaths( - packageManagerInfo, - cacheDependency - ) + cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency) ).rejects.toThrow(); //'Could not get cache folder path for /dir'); } ); @@ -248,7 +203,7 @@ two getCommandOutputSpy.mockImplementationOnce(() => version); getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, '' ); @@ -268,7 +223,7 @@ two MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file']) ); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, '/tmp/**/file' ); @@ -292,7 +247,7 @@ two ]) ); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, '/tmp/**/file' ); @@ -318,7 +273,7 @@ two ]) ); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, '/tmp/**/file' ); @@ -367,13 +322,18 @@ two `; globCreateSpy.mockImplementation( (pattern: string): Promise => - MockGlobber.create(['/tmp/dir3/file', '/tmp/dir4/file']) + MockGlobber.create([ + '/tmp/dir1/file', + '/tmp/dir2/file', + '/tmp/dir3/file', + '/tmp/dir4/file' + ]) ); let dirNo = 1; getCommandOutputSpy.mockImplementation((command: string) => command.includes('version') ? version : `file_${version}_${dirNo++}` ); - const dirs = await cacheUtils.getCacheDirectoriesPaths( + const dirs = await cacheUtils.getCacheDirectories( supportedPackageManagers.yarn, cacheDependencyPath ); diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 631e6d47..b7b72fde 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -60378,7 +60378,7 @@ const cachePackages = (packageManager) => __awaiter(void 0, void 0, void 0, func // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // export declare function getInput(name: string, options?: InputOptions): string; const cacheDependencyPath = core.getInput('cache-dependency-path') || ''; - const cachePaths = yield cache_utils_1.getCacheDirectoriesPaths(packageManagerInfo, cacheDependencyPath); + const cachePaths = yield cache_utils_1.getCacheDirectories(packageManagerInfo, cacheDependencyPath); if (cachePaths.length === 0) { throw new Error(`Cache folder paths are not retrieved for ${packageManager} with cache-dependency-path = ${cacheDependencyPath}`); } @@ -60434,38 +60434,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isCacheFeatureAvailable = exports.isGhes = exports.getCacheDirectoriesPaths = exports.expandCacheDependencyPath = exports.expandedPatternsMemoized = exports.getPackageManagerInfo = exports.getCommandOutputGuarded = exports.getCommandOutput = exports.supportedPackageManagers = exports.yarn2GetCacheFolderCommand = exports.yarn1GetCacheFolderCommand = exports.pnpmGetCacheFolderCommand = exports.npmGetCacheFolderCommand = void 0; +exports.isCacheFeatureAvailable = exports.isGhes = exports.getCacheDirectories = exports.memoizedCacheDependencies = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const cache = __importStar(__nccwpck_require__(7799)); const glob = __importStar(__nccwpck_require__(8090)); const path_1 = __importDefault(__nccwpck_require__(1017)); const fs_1 = __importDefault(__nccwpck_require__(7147)); -// for testing purposes -exports.npmGetCacheFolderCommand = 'npm config get cache'; -exports.pnpmGetCacheFolderCommand = 'pnpm store path --silent'; -exports.yarn1GetCacheFolderCommand = 'yarn cache dir'; -exports.yarn2GetCacheFolderCommand = 'yarn config get cacheFolder'; exports.supportedPackageManagers = { npm: { name: 'npm', lockFilePatterns: ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock'], - getCacheFolderPath: () => exports.getCommandOutputGuarded(exports.npmGetCacheFolderCommand, 'Could not get npm cache folder path') + getCacheFolderPath: () => exports.getCommandOutputNotEmpty('npm config get cache', 'Could not get npm cache folder path') }, pnpm: { name: 'pnpm', lockFilePatterns: ['pnpm-lock.yaml'], - getCacheFolderPath: () => exports.getCommandOutputGuarded(exports.pnpmGetCacheFolderCommand, 'Could not get pnpm cache folder path') + getCacheFolderPath: () => exports.getCommandOutputNotEmpty('pnpm store path --silent', 'Could not get pnpm cache folder path') }, yarn: { name: 'yarn', lockFilePatterns: ['yarn.lock'], getCacheFolderPath: (projectDir) => __awaiter(void 0, void 0, void 0, function* () { - const yarnVersion = yield exports.getCommandOutputGuarded(`yarn --version`, 'Could not retrieve version of yarn', projectDir); - core.debug(`Consumed yarn version is ${yarnVersion} (working dir: "${projectDir}")`); + const yarnVersion = yield exports.getCommandOutputNotEmpty(`yarn --version`, 'Could not retrieve version of yarn', projectDir); + core.debug(`Consumed yarn version is ${yarnVersion} (working dir: "${projectDir || ''}")`); const stdOut = yarnVersion.startsWith('1.') - ? yield exports.getCommandOutput(exports.yarn1GetCacheFolderCommand, projectDir) - : yield exports.getCommandOutput(exports.yarn2GetCacheFolderCommand, projectDir); + ? yield exports.getCommandOutput('yarn cache dir', projectDir) + : yield exports.getCommandOutput('yarn config get cacheFolder', projectDir); if (!stdOut) { throw new Error(`Could not get yarn cache folder path for ${projectDir}`); } @@ -60484,14 +60479,14 @@ const getCommandOutput = (toolCommand, cwd) => __awaiter(void 0, void 0, void 0, return stdout.trim(); }); exports.getCommandOutput = getCommandOutput; -const getCommandOutputGuarded = (toolCommand, error, cwd) => __awaiter(void 0, void 0, void 0, function* () { +const getCommandOutputNotEmpty = (toolCommand, error, cwd) => __awaiter(void 0, void 0, void 0, function* () { const stdOut = exports.getCommandOutput(toolCommand, cwd); if (!stdOut) { throw new Error(error); } return stdOut; }); -exports.getCommandOutputGuarded = getCommandOutputGuarded; +exports.getCommandOutputNotEmpty = getCommandOutputNotEmpty; const getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { if (packageManager === 'npm') { return exports.supportedPackageManagers.npm; @@ -60511,54 +60506,7 @@ exports.getPackageManagerInfo = getPackageManagerInfo; * glob expanding memoized because it involves potentially very deep * traversing through the directories tree */ -exports.expandedPatternsMemoized = {}; -/** - * Wrapper around `glob.create(pattern).glob()` with the memoization - * @param pattern is expected to be a globed path - * @return list of files or directories expanded from glob - */ -const globPatternToArray = (pattern) => __awaiter(void 0, void 0, void 0, function* () { - const memoized = exports.expandedPatternsMemoized[pattern]; - if (memoized) - return Promise.resolve(memoized); - const globber = yield glob.create(pattern); - const expanded = yield globber.glob(); - exports.expandedPatternsMemoized[pattern] = expanded; - return expanded; -}); -/** - * Expands (converts) the string input `cache-dependency-path` to list of files' paths - * First it breaks the input by new lines and then expand glob patterns if any - * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns - * @return list of files on which the cache depends - */ -const expandCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const multilinePaths = cacheDependencyPath - .split(/\r?\n/) - .map(path => path.trim()) - .filter(path => Boolean(path)); - const expandedPathsPromises = multilinePaths.map(path => path.includes('*') ? globPatternToArray(path) : Promise.resolve([path])); - const expandedPaths = yield Promise.all(expandedPathsPromises); - return expandedPaths.length === 0 ? [''] : expandedPaths.flat(); -}); -exports.expandCacheDependencyPath = expandCacheDependencyPath; -/** - * Converts dependency file path to the directory it resides in and ensures the directory exists - * @param cacheDependencyPath - a file name path - * @return either directory containing the file or null - */ -const cacheDependencyPathToProjectDirectory = (cacheDependencyPath) => { - const projectDirectory = path_1.default.dirname(cacheDependencyPath); - if (fs_1.default.existsSync(projectDirectory) && - fs_1.default.lstatSync(projectDirectory).isDirectory()) { - core.debug(`Project directory "${projectDirectory}" derived from cache-dependency-path: "${cacheDependencyPath}"`); - return projectDirectory; - } - else { - core.debug(`No project directory found for cache-dependency-path: "${cacheDependencyPath}", will be skipped`); - return null; - } -}; +exports.memoizedCacheDependencies = {}; /** * Expands (converts) the string input `cache-dependency-path` to list of directories that * may be project roots @@ -60566,69 +60514,76 @@ const cacheDependencyPathToProjectDirectory = (cacheDependencyPath) => { * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of directories and possible */ -const cacheDependencyPathToProjectsDirectories = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const cacheDependenciesPaths = yield exports.expandCacheDependencyPath(cacheDependencyPath); +const getProjectDirectoriesFromCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + let cacheDependenciesPaths; + // memoize unglobbed paths to avoid traversing FS + const memoized = exports.memoizedCacheDependencies[cacheDependencyPath]; + if (memoized) { + cacheDependenciesPaths = memoized; + } + else { + cacheDependenciesPaths = (yield glob + .create(cacheDependencyPath) + .then(globber => globber.glob())) || ['']; + exports.memoizedCacheDependencies[cacheDependencyPath] = cacheDependenciesPaths; + } const existingDirectories = cacheDependenciesPaths - .map(cacheDependencyPath => cacheDependencyPathToProjectDirectory(cacheDependencyPath)) - .filter(path => path !== null); + .map(cacheDependencyPath => path_1.default.dirname(cacheDependencyPath)) + // uniq in order to do not traverse the same directories during the further processing + .filter((cachePath, i, result) => cachePath != null && result.indexOf(cachePath) === i) + .filter(directory => fs_1.default.existsSync(directory) && fs_1.default.lstatSync(directory).isDirectory()); // if user explicitly pointed out some file, but it does not exist it is definitely // not he wanted, thus we should throw an error not trying to workaround with unexpected // result to the whole build if (existingDirectories.length === 0) throw Error('No existing directories found containing `cache-dependency-path`="${cacheDependencyPath}"'); - // uniq in order to do not traverse the same directories during the further processing - return existingDirectories.filter((cachePath, i, result) => cachePath != null && result.indexOf(cachePath) === i); + return existingDirectories; }); /** - * Utility function to be used from within `map` - * Finds the cache directories configured for the project directory - * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM - * @param projectDirectory - the string pointing out to a project dir (i.e. directory with its own .yarnrc) - * @return list of directories to be cached according to the project configuration in the directory - */ -const projectDirectoryToCacheFolderPath = (packageManagerInfo, projectDirectory) => __awaiter(void 0, void 0, void 0, function* () { - const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath(projectDirectory); - core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`); - return cacheFolderPath; -}); -/** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is not empty + * Finds the cache directories configured for the repo if cache-dependency-path is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -const cacheDependencyPathToCacheFoldersPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const projectDirectories = yield cacheDependencyPathToProjectsDirectories(cacheDependencyPath); - const cacheFoldersPaths = yield Promise.all(projectDirectories.map(projectDirectory => projectDirectoryToCacheFolderPath(packageManagerInfo, projectDirectory))); +const getCacheDirectoriesFromCacheDependencyPath = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + const projectDirectories = yield getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath); + const cacheFoldersPaths = yield Promise.all(projectDirectories.map(projectDirectory => packageManagerInfo + .getCacheFolderPath(projectDirectory) + .then(cacheFolderPath => { + core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`); + return cacheFolderPath; + }))); // uniq in order to do not cache the same directories twice return cacheFoldersPaths.filter((cachePath, i, result) => result.indexOf(cachePath) === i); }); /** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is empty + * Finds the cache directories configured for the repo ignoring cache-dependency-path * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @return list of files on which the cache depends */ -const cacheFoldersPathsForRoot = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { +const getCacheDirectoriesForRootProject = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath(); core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the root directory`); return [cacheFolderPath]; }); /** - * Main function to find the cache directories configured for the repo + * A function to find the cache directories configured for the repo * currently it handles only the case of PM=yarn && cacheDependencyPath is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -const getCacheDirectoriesPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - // TODO: multiple directories limited to yarn so far - return packageManagerInfo === exports.supportedPackageManagers.yarn - ? cacheDependencyPathToCacheFoldersPaths(packageManagerInfo, cacheDependencyPath) - : cacheFoldersPathsForRoot(packageManagerInfo); +const getCacheDirectories = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + // For yarn, if cacheDependencyPath is set, ask information about cache folders in each project + // folder satisfied by cacheDependencyPath https://github.com/actions/setup-node/issues/488 + if (packageManagerInfo.name === 'yarn' && cacheDependencyPath) { + return getCacheDirectoriesFromCacheDependencyPath(packageManagerInfo, cacheDependencyPath); + } + return getCacheDirectoriesForRootProject(packageManagerInfo); }); -exports.getCacheDirectoriesPaths = getCacheDirectoriesPaths; +exports.getCacheDirectories = getCacheDirectories; function isGhes() { const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; diff --git a/dist/setup/index.js b/dist/setup/index.js index 23abc160..39a3cef8 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -71144,7 +71144,7 @@ const restoreCache = (packageManager, cacheDependencyPath) => __awaiter(void 0, throw new Error(`Caching for '${packageManager}' is not supported`); } const platform = process.env.RUNNER_OS; - const cachePaths = yield cache_utils_1.getCacheDirectoriesPaths(packageManagerInfo, cacheDependencyPath); + const cachePaths = yield cache_utils_1.getCacheDirectories(packageManagerInfo, cacheDependencyPath); const lockFilePath = cacheDependencyPath ? cacheDependencyPath : findLockFile(packageManagerInfo); @@ -71216,38 +71216,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isCacheFeatureAvailable = exports.isGhes = exports.getCacheDirectoriesPaths = exports.expandCacheDependencyPath = exports.expandedPatternsMemoized = exports.getPackageManagerInfo = exports.getCommandOutputGuarded = exports.getCommandOutput = exports.supportedPackageManagers = exports.yarn2GetCacheFolderCommand = exports.yarn1GetCacheFolderCommand = exports.pnpmGetCacheFolderCommand = exports.npmGetCacheFolderCommand = void 0; +exports.isCacheFeatureAvailable = exports.isGhes = exports.getCacheDirectories = exports.memoizedCacheDependencies = exports.getPackageManagerInfo = exports.getCommandOutputNotEmpty = exports.getCommandOutput = exports.supportedPackageManagers = void 0; const core = __importStar(__nccwpck_require__(2186)); const exec = __importStar(__nccwpck_require__(1514)); const cache = __importStar(__nccwpck_require__(7799)); const glob = __importStar(__nccwpck_require__(8090)); const path_1 = __importDefault(__nccwpck_require__(1017)); const fs_1 = __importDefault(__nccwpck_require__(7147)); -// for testing purposes -exports.npmGetCacheFolderCommand = 'npm config get cache'; -exports.pnpmGetCacheFolderCommand = 'pnpm store path --silent'; -exports.yarn1GetCacheFolderCommand = 'yarn cache dir'; -exports.yarn2GetCacheFolderCommand = 'yarn config get cacheFolder'; exports.supportedPackageManagers = { npm: { name: 'npm', lockFilePatterns: ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock'], - getCacheFolderPath: () => exports.getCommandOutputGuarded(exports.npmGetCacheFolderCommand, 'Could not get npm cache folder path') + getCacheFolderPath: () => exports.getCommandOutputNotEmpty('npm config get cache', 'Could not get npm cache folder path') }, pnpm: { name: 'pnpm', lockFilePatterns: ['pnpm-lock.yaml'], - getCacheFolderPath: () => exports.getCommandOutputGuarded(exports.pnpmGetCacheFolderCommand, 'Could not get pnpm cache folder path') + getCacheFolderPath: () => exports.getCommandOutputNotEmpty('pnpm store path --silent', 'Could not get pnpm cache folder path') }, yarn: { name: 'yarn', lockFilePatterns: ['yarn.lock'], getCacheFolderPath: (projectDir) => __awaiter(void 0, void 0, void 0, function* () { - const yarnVersion = yield exports.getCommandOutputGuarded(`yarn --version`, 'Could not retrieve version of yarn', projectDir); - core.debug(`Consumed yarn version is ${yarnVersion} (working dir: "${projectDir}")`); + const yarnVersion = yield exports.getCommandOutputNotEmpty(`yarn --version`, 'Could not retrieve version of yarn', projectDir); + core.debug(`Consumed yarn version is ${yarnVersion} (working dir: "${projectDir || ''}")`); const stdOut = yarnVersion.startsWith('1.') - ? yield exports.getCommandOutput(exports.yarn1GetCacheFolderCommand, projectDir) - : yield exports.getCommandOutput(exports.yarn2GetCacheFolderCommand, projectDir); + ? yield exports.getCommandOutput('yarn cache dir', projectDir) + : yield exports.getCommandOutput('yarn config get cacheFolder', projectDir); if (!stdOut) { throw new Error(`Could not get yarn cache folder path for ${projectDir}`); } @@ -71266,14 +71261,14 @@ const getCommandOutput = (toolCommand, cwd) => __awaiter(void 0, void 0, void 0, return stdout.trim(); }); exports.getCommandOutput = getCommandOutput; -const getCommandOutputGuarded = (toolCommand, error, cwd) => __awaiter(void 0, void 0, void 0, function* () { +const getCommandOutputNotEmpty = (toolCommand, error, cwd) => __awaiter(void 0, void 0, void 0, function* () { const stdOut = exports.getCommandOutput(toolCommand, cwd); if (!stdOut) { throw new Error(error); } return stdOut; }); -exports.getCommandOutputGuarded = getCommandOutputGuarded; +exports.getCommandOutputNotEmpty = getCommandOutputNotEmpty; const getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void 0, function* () { if (packageManager === 'npm') { return exports.supportedPackageManagers.npm; @@ -71293,54 +71288,7 @@ exports.getPackageManagerInfo = getPackageManagerInfo; * glob expanding memoized because it involves potentially very deep * traversing through the directories tree */ -exports.expandedPatternsMemoized = {}; -/** - * Wrapper around `glob.create(pattern).glob()` with the memoization - * @param pattern is expected to be a globed path - * @return list of files or directories expanded from glob - */ -const globPatternToArray = (pattern) => __awaiter(void 0, void 0, void 0, function* () { - const memoized = exports.expandedPatternsMemoized[pattern]; - if (memoized) - return Promise.resolve(memoized); - const globber = yield glob.create(pattern); - const expanded = yield globber.glob(); - exports.expandedPatternsMemoized[pattern] = expanded; - return expanded; -}); -/** - * Expands (converts) the string input `cache-dependency-path` to list of files' paths - * First it breaks the input by new lines and then expand glob patterns if any - * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns - * @return list of files on which the cache depends - */ -const expandCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const multilinePaths = cacheDependencyPath - .split(/\r?\n/) - .map(path => path.trim()) - .filter(path => Boolean(path)); - const expandedPathsPromises = multilinePaths.map(path => path.includes('*') ? globPatternToArray(path) : Promise.resolve([path])); - const expandedPaths = yield Promise.all(expandedPathsPromises); - return expandedPaths.length === 0 ? [''] : expandedPaths.flat(); -}); -exports.expandCacheDependencyPath = expandCacheDependencyPath; -/** - * Converts dependency file path to the directory it resides in and ensures the directory exists - * @param cacheDependencyPath - a file name path - * @return either directory containing the file or null - */ -const cacheDependencyPathToProjectDirectory = (cacheDependencyPath) => { - const projectDirectory = path_1.default.dirname(cacheDependencyPath); - if (fs_1.default.existsSync(projectDirectory) && - fs_1.default.lstatSync(projectDirectory).isDirectory()) { - core.debug(`Project directory "${projectDirectory}" derived from cache-dependency-path: "${cacheDependencyPath}"`); - return projectDirectory; - } - else { - core.debug(`No project directory found for cache-dependency-path: "${cacheDependencyPath}", will be skipped`); - return null; - } -}; +exports.memoizedCacheDependencies = {}; /** * Expands (converts) the string input `cache-dependency-path` to list of directories that * may be project roots @@ -71348,69 +71296,76 @@ const cacheDependencyPathToProjectDirectory = (cacheDependencyPath) => { * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of directories and possible */ -const cacheDependencyPathToProjectsDirectories = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const cacheDependenciesPaths = yield exports.expandCacheDependencyPath(cacheDependencyPath); +const getProjectDirectoriesFromCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + let cacheDependenciesPaths; + // memoize unglobbed paths to avoid traversing FS + const memoized = exports.memoizedCacheDependencies[cacheDependencyPath]; + if (memoized) { + cacheDependenciesPaths = memoized; + } + else { + cacheDependenciesPaths = (yield glob + .create(cacheDependencyPath) + .then(globber => globber.glob())) || ['']; + exports.memoizedCacheDependencies[cacheDependencyPath] = cacheDependenciesPaths; + } const existingDirectories = cacheDependenciesPaths - .map(cacheDependencyPath => cacheDependencyPathToProjectDirectory(cacheDependencyPath)) - .filter(path => path !== null); + .map(cacheDependencyPath => path_1.default.dirname(cacheDependencyPath)) + // uniq in order to do not traverse the same directories during the further processing + .filter((cachePath, i, result) => cachePath != null && result.indexOf(cachePath) === i) + .filter(directory => fs_1.default.existsSync(directory) && fs_1.default.lstatSync(directory).isDirectory()); // if user explicitly pointed out some file, but it does not exist it is definitely // not he wanted, thus we should throw an error not trying to workaround with unexpected // result to the whole build if (existingDirectories.length === 0) throw Error('No existing directories found containing `cache-dependency-path`="${cacheDependencyPath}"'); - // uniq in order to do not traverse the same directories during the further processing - return existingDirectories.filter((cachePath, i, result) => cachePath != null && result.indexOf(cachePath) === i); + return existingDirectories; }); /** - * Utility function to be used from within `map` - * Finds the cache directories configured for the project directory - * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM - * @param projectDirectory - the string pointing out to a project dir (i.e. directory with its own .yarnrc) - * @return list of directories to be cached according to the project configuration in the directory - */ -const projectDirectoryToCacheFolderPath = (packageManagerInfo, projectDirectory) => __awaiter(void 0, void 0, void 0, function* () { - const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath(projectDirectory); - core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`); - return cacheFolderPath; -}); -/** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is not empty + * Finds the cache directories configured for the repo if cache-dependency-path is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -const cacheDependencyPathToCacheFoldersPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - const projectDirectories = yield cacheDependencyPathToProjectsDirectories(cacheDependencyPath); - const cacheFoldersPaths = yield Promise.all(projectDirectories.map(projectDirectory => projectDirectoryToCacheFolderPath(packageManagerInfo, projectDirectory))); +const getCacheDirectoriesFromCacheDependencyPath = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + const projectDirectories = yield getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath); + const cacheFoldersPaths = yield Promise.all(projectDirectories.map(projectDirectory => packageManagerInfo + .getCacheFolderPath(projectDirectory) + .then(cacheFolderPath => { + core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`); + return cacheFolderPath; + }))); // uniq in order to do not cache the same directories twice return cacheFoldersPaths.filter((cachePath, i, result) => result.indexOf(cachePath) === i); }); /** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is empty + * Finds the cache directories configured for the repo ignoring cache-dependency-path * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @return list of files on which the cache depends */ -const cacheFoldersPathsForRoot = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { +const getCacheDirectoriesForRootProject = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () { const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath(); core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the root directory`); return [cacheFolderPath]; }); /** - * Main function to find the cache directories configured for the repo + * A function to find the cache directories configured for the repo * currently it handles only the case of PM=yarn && cacheDependencyPath is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -const getCacheDirectoriesPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { - // TODO: multiple directories limited to yarn so far - return packageManagerInfo === exports.supportedPackageManagers.yarn - ? cacheDependencyPathToCacheFoldersPaths(packageManagerInfo, cacheDependencyPath) - : cacheFoldersPathsForRoot(packageManagerInfo); +const getCacheDirectories = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () { + // For yarn, if cacheDependencyPath is set, ask information about cache folders in each project + // folder satisfied by cacheDependencyPath https://github.com/actions/setup-node/issues/488 + if (packageManagerInfo.name === 'yarn' && cacheDependencyPath) { + return getCacheDirectoriesFromCacheDependencyPath(packageManagerInfo, cacheDependencyPath); + } + return getCacheDirectoriesForRootProject(packageManagerInfo); }); -exports.getCacheDirectoriesPaths = getCacheDirectoriesPaths; +exports.getCacheDirectories = getCacheDirectories; function isGhes() { const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; diff --git a/src/cache-restore.ts b/src/cache-restore.ts index 801d525c..cddc4127 100644 --- a/src/cache-restore.ts +++ b/src/cache-restore.ts @@ -6,7 +6,7 @@ import fs from 'fs'; import {State} from './constants'; import { - getCacheDirectoriesPaths, + getCacheDirectories, getPackageManagerInfo, PackageManagerInfo } from './cache-utils'; @@ -21,7 +21,7 @@ export const restoreCache = async ( } const platform = process.env.RUNNER_OS; - const cachePaths = await getCacheDirectoriesPaths( + const cachePaths = await getCacheDirectories( packageManagerInfo, cacheDependencyPath ); diff --git a/src/cache-save.ts b/src/cache-save.ts index a54a741d..6b53a904 100644 --- a/src/cache-save.ts +++ b/src/cache-save.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core'; import * as cache from '@actions/cache'; import {State} from './constants'; -import {getCacheDirectoriesPaths, getPackageManagerInfo} from './cache-utils'; +import {getCacheDirectories, getPackageManagerInfo} 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 @@ -33,7 +33,7 @@ const cachePackages = async (packageManager: string) => { // TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?) // export declare function getInput(name: string, options?: InputOptions): string; const cacheDependencyPath = core.getInput('cache-dependency-path') || ''; - const cachePaths = await getCacheDirectoriesPaths( + const cachePaths = await getCacheDirectories( packageManagerInfo, cacheDependencyPath ); diff --git a/src/cache-utils.ts b/src/cache-utils.ts index 4fc69717..2d244b65 100644 --- a/src/cache-utils.ts +++ b/src/cache-utils.ts @@ -16,19 +16,13 @@ interface SupportedPackageManagers { pnpm: PackageManagerInfo; yarn: PackageManagerInfo; } - -// for testing purposes -export const npmGetCacheFolderCommand = 'npm config get cache'; -export const pnpmGetCacheFolderCommand = 'pnpm store path --silent'; -export const yarn1GetCacheFolderCommand = 'yarn cache dir'; -export const yarn2GetCacheFolderCommand = 'yarn config get cacheFolder'; export const supportedPackageManagers: SupportedPackageManagers = { npm: { name: 'npm', lockFilePatterns: ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock'], getCacheFolderPath: () => - getCommandOutputGuarded( - npmGetCacheFolderCommand, + getCommandOutputNotEmpty( + 'npm config get cache', 'Could not get npm cache folder path' ) }, @@ -36,8 +30,8 @@ export const supportedPackageManagers: SupportedPackageManagers = { name: 'pnpm', lockFilePatterns: ['pnpm-lock.yaml'], getCacheFolderPath: () => - getCommandOutputGuarded( - pnpmGetCacheFolderCommand, + getCommandOutputNotEmpty( + 'pnpm store path --silent', 'Could not get pnpm cache folder path' ) }, @@ -45,19 +39,21 @@ export const supportedPackageManagers: SupportedPackageManagers = { name: 'yarn', lockFilePatterns: ['yarn.lock'], getCacheFolderPath: async projectDir => { - const yarnVersion = await getCommandOutputGuarded( + const yarnVersion = await getCommandOutputNotEmpty( `yarn --version`, 'Could not retrieve version of yarn', projectDir ); core.debug( - `Consumed yarn version is ${yarnVersion} (working dir: "${projectDir}")` + `Consumed yarn version is ${yarnVersion} (working dir: "${ + projectDir || '' + }")` ); const stdOut = yarnVersion.startsWith('1.') - ? await getCommandOutput(yarn1GetCacheFolderCommand, projectDir) - : await getCommandOutput(yarn2GetCacheFolderCommand, projectDir); + ? await getCommandOutput('yarn cache dir', projectDir) + : await getCommandOutput('yarn config get cacheFolder', projectDir); if (!stdOut) { throw new Error( @@ -89,7 +85,7 @@ export const getCommandOutput = async ( return stdout.trim(); }; -export const getCommandOutputGuarded = async ( +export const getCommandOutputNotEmpty = async ( toolCommand: string, error: string, cwd?: string @@ -117,66 +113,7 @@ export const getPackageManagerInfo = async (packageManager: string) => { * glob expanding memoized because it involves potentially very deep * traversing through the directories tree */ -export const expandedPatternsMemoized: Record = {}; -/** - * Wrapper around `glob.create(pattern).glob()` with the memoization - * @param pattern is expected to be a globed path - * @return list of files or directories expanded from glob - */ -const globPatternToArray = async (pattern: string): Promise => { - const memoized = expandedPatternsMemoized[pattern]; - if (memoized) return Promise.resolve(memoized); - const globber = await glob.create(pattern); - const expanded = await globber.glob(); - expandedPatternsMemoized[pattern] = expanded; - return expanded; -}; - -/** - * Expands (converts) the string input `cache-dependency-path` to list of files' paths - * First it breaks the input by new lines and then expand glob patterns if any - * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns - * @return list of files on which the cache depends - */ -export const expandCacheDependencyPath = async ( - cacheDependencyPath: string -): Promise => { - const multilinePaths = cacheDependencyPath - .split(/\r?\n/) - .map(path => path.trim()) - .filter(path => Boolean(path)); - const expandedPathsPromises: Promise[] = multilinePaths.map(path => - path.includes('*') ? globPatternToArray(path) : Promise.resolve([path]) - ); - const expandedPaths: string[][] = await Promise.all(expandedPathsPromises); - return expandedPaths.length === 0 ? [''] : expandedPaths.flat(); -}; - -/** - * Converts dependency file path to the directory it resides in and ensures the directory exists - * @param cacheDependencyPath - a file name path - * @return either directory containing the file or null - */ -const cacheDependencyPathToProjectDirectory = ( - cacheDependencyPath: string -): string | null => { - const projectDirectory = path.dirname(cacheDependencyPath); - if ( - fs.existsSync(projectDirectory) && - fs.lstatSync(projectDirectory).isDirectory() - ) { - core.debug( - `Project directory "${projectDirectory}" derived from cache-dependency-path: "${cacheDependencyPath}"` - ); - return projectDirectory; - } else { - core.debug( - `No project directory found for cache-dependency-path: "${cacheDependencyPath}", will be skipped` - ); - return null; - } -}; - +export const memoizedCacheDependencies: Record = {}; /** * Expands (converts) the string input `cache-dependency-path` to list of directories that * may be project roots @@ -184,18 +121,33 @@ const cacheDependencyPathToProjectDirectory = ( * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of directories and possible */ -const cacheDependencyPathToProjectsDirectories = async ( +const getProjectDirectoriesFromCacheDependencyPath = async ( cacheDependencyPath: string ): Promise => { - const cacheDependenciesPaths = await expandCacheDependencyPath( - cacheDependencyPath - ); + let cacheDependenciesPaths: string[]; + + // memoize unglobbed paths to avoid traversing FS + const memoized = memoizedCacheDependencies[cacheDependencyPath]; + if (memoized) { + cacheDependenciesPaths = memoized; + } else { + cacheDependenciesPaths = (await glob + .create(cacheDependencyPath) + .then(globber => globber.glob())) || ['']; + memoizedCacheDependencies[cacheDependencyPath] = cacheDependenciesPaths; + } const existingDirectories: string[] = cacheDependenciesPaths - .map(cacheDependencyPath => - cacheDependencyPathToProjectDirectory(cacheDependencyPath) + .map(cacheDependencyPath => path.dirname(cacheDependencyPath)) + // uniq in order to do not traverse the same directories during the further processing + .filter( + (cachePath, i, result) => + cachePath != null && result.indexOf(cachePath) === i ) - .filter(path => path !== null) as string[]; + .filter( + directory => + fs.existsSync(directory) && fs.lstatSync(directory).isDirectory() + ) as string[]; // if user explicitly pointed out some file, but it does not exist it is definitely // not he wanted, thus we should throw an error not trying to workaround with unexpected @@ -205,50 +157,33 @@ const cacheDependencyPathToProjectsDirectories = async ( 'No existing directories found containing `cache-dependency-path`="${cacheDependencyPath}"' ); - // uniq in order to do not traverse the same directories during the further processing - return existingDirectories.filter( - (cachePath, i, result) => - cachePath != null && result.indexOf(cachePath) === i - ); + return existingDirectories; }; /** - * Utility function to be used from within `map` - * Finds the cache directories configured for the project directory - * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM - * @param projectDirectory - the string pointing out to a project dir (i.e. directory with its own .yarnrc) - * @return list of directories to be cached according to the project configuration in the directory - */ -const projectDirectoryToCacheFolderPath = async ( - packageManagerInfo: PackageManagerInfo, - projectDirectory: string -): Promise => { - const cacheFolderPath = await packageManagerInfo.getCacheFolderPath( - projectDirectory - ); - core.debug( - `${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"` - ); - return cacheFolderPath; -}; - -/** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is not empty + * Finds the cache directories configured for the repo if cache-dependency-path is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -const cacheDependencyPathToCacheFoldersPaths = async ( +const getCacheDirectoriesFromCacheDependencyPath = async ( packageManagerInfo: PackageManagerInfo, cacheDependencyPath: string ): Promise => { - const projectDirectories = await cacheDependencyPathToProjectsDirectories( + const projectDirectories = await getProjectDirectoriesFromCacheDependencyPath( cacheDependencyPath ); const cacheFoldersPaths = await Promise.all( projectDirectories.map(projectDirectory => - projectDirectoryToCacheFolderPath(packageManagerInfo, projectDirectory) + packageManagerInfo + .getCacheFolderPath(projectDirectory) + .then(cacheFolderPath => { + core.debug( + `${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"` + ); + return cacheFolderPath; + }) ) ); // uniq in order to do not cache the same directories twice @@ -258,11 +193,11 @@ const cacheDependencyPathToCacheFoldersPaths = async ( }; /** - * Top-entry function to find the cache directories configured for the repo if cache-dependency-path is empty + * Finds the cache directories configured for the repo ignoring cache-dependency-path * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @return list of files on which the cache depends */ -const cacheFoldersPathsForRoot = async ( +const getCacheDirectoriesForRootProject = async ( packageManagerInfo: PackageManagerInfo ): Promise => { const cacheFolderPath = await packageManagerInfo.getCacheFolderPath(); @@ -273,24 +208,27 @@ const cacheFoldersPathsForRoot = async ( }; /** - * Main function to find the cache directories configured for the repo + * A function to find the cache directories configured for the repo * currently it handles only the case of PM=yarn && cacheDependencyPath is not empty * @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM * @param cacheDependencyPath - either a single string or multiline string with possible glob patterns * expected to be the result of `core.getInput('cache-dependency-path')` * @return list of files on which the cache depends */ -export const getCacheDirectoriesPaths = async ( +export const getCacheDirectories = async ( packageManagerInfo: PackageManagerInfo, cacheDependencyPath: string -): Promise => - // TODO: multiple directories limited to yarn so far - packageManagerInfo === supportedPackageManagers.yarn - ? cacheDependencyPathToCacheFoldersPaths( - packageManagerInfo, - cacheDependencyPath - ) - : cacheFoldersPathsForRoot(packageManagerInfo); +): Promise => { + // For yarn, if cacheDependencyPath is set, ask information about cache folders in each project + // folder satisfied by cacheDependencyPath https://github.com/actions/setup-node/issues/488 + if (packageManagerInfo.name === 'yarn' && cacheDependencyPath) { + return getCacheDirectoriesFromCacheDependencyPath( + packageManagerInfo, + cacheDependencyPath + ); + } + return getCacheDirectoriesForRootProject(packageManagerInfo); +}; export function isGhes(): boolean { const ghUrl = new URL(