Skip to content

Commit 47381ae

Browse files
committed
refactor, introduce cacheDependencyPathToProjectsDirectories
it is necessary for the next PR related yarn optimization
1 parent b779602 commit 47381ae

File tree

5 files changed

+293
-81
lines changed

5 files changed

+293
-81
lines changed

__tests__/cache-save.test.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,14 @@ describe('run', () => {
118118
expect(getStateSpy).toHaveBeenCalledTimes(2);
119119
expect(getCommandOutputSpy).toHaveBeenCalledTimes(2);
120120
expect(debugSpy).toHaveBeenCalledWith(
121-
'yarn path is /some/random/path/yarn1 (derived from cache-dependency-path: "")'
121+
'Project directory "." derived from cache-dependency-path: ""'
122+
);
123+
expect(debugSpy).toHaveBeenCalledWith(
124+
'Consumed yarn version is 1.2.3 (working dir: ".")'
125+
);
126+
expect(debugSpy).toHaveBeenCalledWith(
127+
'yarn\'s cache folder "/some/random/path/yarn1" configured for the directory "."'
122128
);
123-
expect(debugSpy).toHaveBeenCalledWith('Consumed yarn version is 1.2.3');
124129
expect(infoSpy).toHaveBeenCalledWith(
125130
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
126131
);
@@ -140,9 +145,14 @@ describe('run', () => {
140145
expect(getStateSpy).toHaveBeenCalledTimes(2);
141146
expect(getCommandOutputSpy).toHaveBeenCalledTimes(2);
142147
expect(debugSpy).toHaveBeenCalledWith(
143-
'yarn path is /some/random/path/yarn2 (derived from cache-dependency-path: "")'
148+
'Project directory "." derived from cache-dependency-path: ""'
149+
);
150+
expect(debugSpy).toHaveBeenCalledWith(
151+
'Consumed yarn version is 2.2.3 (working dir: ".")'
152+
);
153+
expect(debugSpy).toHaveBeenCalledWith(
154+
'yarn\'s cache folder "/some/random/path/yarn2" configured for the directory "."'
144155
);
145-
expect(debugSpy).toHaveBeenCalledWith('Consumed yarn version is 2.2.3');
146156
expect(infoSpy).toHaveBeenCalledWith(
147157
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
148158
);
@@ -159,7 +169,9 @@ describe('run', () => {
159169
expect(getInputSpy).toHaveBeenCalled();
160170
expect(getStateSpy).toHaveBeenCalledTimes(2);
161171
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
162-
expect(debugSpy).toHaveBeenCalledWith(`npm path is ${commonPath}/npm`);
172+
expect(debugSpy).toHaveBeenCalledWith(
173+
`npm's cache folder "${commonPath}/npm" configured for the root directory`
174+
);
163175
expect(infoSpy).toHaveBeenCalledWith(
164176
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
165177
);
@@ -176,7 +188,9 @@ describe('run', () => {
176188
expect(getInputSpy).toHaveBeenCalled();
177189
expect(getStateSpy).toHaveBeenCalledTimes(2);
178190
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
179-
expect(debugSpy).toHaveBeenCalledWith(`pnpm path is ${commonPath}/pnpm`);
191+
expect(debugSpy).toHaveBeenCalledWith(
192+
`pnpm's cache folder "${commonPath}/pnpm" configured for the root directory`
193+
);
180194
expect(infoSpy).toHaveBeenCalledWith(
181195
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
182196
);
@@ -204,9 +218,14 @@ describe('run', () => {
204218
expect(getStateSpy).toHaveBeenCalledTimes(2);
205219
expect(getCommandOutputSpy).toHaveBeenCalledTimes(2);
206220
expect(debugSpy).toHaveBeenCalledWith(
207-
'yarn path is /some/random/path/yarn1 (derived from cache-dependency-path: "")'
221+
'Project directory "." derived from cache-dependency-path: ""'
222+
);
223+
expect(debugSpy).toHaveBeenCalledWith(
224+
'Consumed yarn version is 1.2.3 (working dir: ".")'
225+
);
226+
expect(debugSpy).toHaveBeenCalledWith(
227+
'yarn\'s cache folder "/some/random/path/yarn1" configured for the directory "."'
208228
);
209-
expect(debugSpy).toHaveBeenCalledWith('Consumed yarn version is 1.2.3');
210229
expect(infoSpy).not.toHaveBeenCalledWith(
211230
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
212231
);
@@ -236,9 +255,14 @@ describe('run', () => {
236255
expect(getStateSpy).toHaveBeenCalledTimes(2);
237256
expect(getCommandOutputSpy).toHaveBeenCalledTimes(2);
238257
expect(debugSpy).toHaveBeenCalledWith(
239-
'yarn path is /some/random/path/yarn2 (derived from cache-dependency-path: "")'
258+
'Project directory "." derived from cache-dependency-path: ""'
259+
);
260+
expect(debugSpy).toHaveBeenCalledWith(
261+
'Consumed yarn version is 2.2.3 (working dir: ".")'
262+
);
263+
expect(debugSpy).toHaveBeenCalledWith(
264+
'yarn\'s cache folder "/some/random/path/yarn2" configured for the directory "."'
240265
);
241-
expect(debugSpy).toHaveBeenCalledWith('Consumed yarn version is 2.2.3');
242266
expect(infoSpy).not.toHaveBeenCalledWith(
243267
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
244268
);
@@ -265,7 +289,9 @@ describe('run', () => {
265289
expect(getInputSpy).toHaveBeenCalled();
266290
expect(getStateSpy).toHaveBeenCalledTimes(2);
267291
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
268-
expect(debugSpy).toHaveBeenCalledWith(`npm path is ${commonPath}/npm`);
292+
expect(debugSpy).toHaveBeenCalledWith(
293+
`npm's cache folder "${commonPath}/npm" configured for the root directory`
294+
);
269295
expect(infoSpy).not.toHaveBeenCalledWith(
270296
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
271297
);
@@ -292,7 +318,9 @@ describe('run', () => {
292318
expect(getInputSpy).toHaveBeenCalled();
293319
expect(getStateSpy).toHaveBeenCalledTimes(2);
294320
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
295-
expect(debugSpy).toHaveBeenCalledWith(`pnpm path is ${commonPath}/pnpm`);
321+
expect(debugSpy).toHaveBeenCalledWith(
322+
`pnpm's cache folder "${commonPath}/pnpm" configured for the root directory`
323+
);
296324
expect(infoSpy).not.toHaveBeenCalledWith(
297325
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
298326
);
@@ -322,7 +350,9 @@ describe('run', () => {
322350
expect(getInputSpy).toHaveBeenCalled();
323351
expect(getStateSpy).toHaveBeenCalledTimes(2);
324352
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
325-
expect(debugSpy).toHaveBeenCalledWith(`npm path is ${commonPath}/npm`);
353+
expect(debugSpy).toHaveBeenCalledWith(
354+
`npm's cache folder "${commonPath}/npm" configured for the root directory`
355+
);
326356
expect(infoSpy).not.toHaveBeenCalledWith(
327357
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
328358
);
@@ -352,7 +382,9 @@ describe('run', () => {
352382
expect(getInputSpy).toHaveBeenCalled();
353383
expect(getStateSpy).toHaveBeenCalledTimes(2);
354384
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
355-
expect(debugSpy).toHaveBeenCalledWith(`npm path is ${commonPath}/npm`);
385+
expect(debugSpy).toHaveBeenCalledWith(
386+
`npm's cache folder "${commonPath}/npm" configured for the root directory`
387+
);
356388
expect(infoSpy).not.toHaveBeenCalledWith(
357389
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
358390
);

__tests__/cache-utils.test.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
isCacheFeatureAvailable,
88
supportedPackageManagers,
99
getCommandOutput,
10-
expandCacheDependencyPath
10+
expandCacheDependencyPath,
11+
expandedPatternsMemoized
1112
} from '../src/cache-utils';
1213
import fs from 'fs';
1314
import * as cacheUtils from '../src/cache-utils';
@@ -104,6 +105,10 @@ describe('cache-utils', () => {
104105
(pattern: string): Promise<Globber> =>
105106
MockGlobber.create(['/foo', '/bar'])
106107
);
108+
109+
Object.keys(expandedPatternsMemoized).forEach(
110+
key => delete expandedPatternsMemoized[key]
111+
);
107112
});
108113

109114
afterEach(() => {
@@ -194,13 +199,36 @@ two
194199
[supportedPackageManagers.yarn, '/dir/file.lock'],
195200
[supportedPackageManagers.yarn, '/**/file.lock']
196201
])(
197-
'getCacheDirectoriesPaths should return empty array of folder in case of error',
202+
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
198203
async (packageManagerInfo, cacheDependency) => {
199204
getCommandOutputSpy.mockImplementation((command: string) =>
200205
// return empty string to indicate getCacheFolderPath failed
201206
// --version still works
202207
command.includes('version') ? '1.' : ''
203208
);
209+
210+
await expect(
211+
cacheUtils.getCacheDirectoriesPaths(
212+
packageManagerInfo,
213+
cacheDependency
214+
)
215+
).rejects.toThrow(); //'Could not get cache folder path for /dir');
216+
}
217+
);
218+
219+
it.each([
220+
[supportedPackageManagers.npm, ''],
221+
[supportedPackageManagers.npm, '/dir/file.lock'],
222+
[supportedPackageManagers.npm, '/**/file.lock'],
223+
[supportedPackageManagers.pnpm, ''],
224+
[supportedPackageManagers.pnpm, '/dir/file.lock'],
225+
[supportedPackageManagers.pnpm, '/**/file.lock'],
226+
[supportedPackageManagers.yarn, ''],
227+
[supportedPackageManagers.yarn, '/dir/file.lock'],
228+
[supportedPackageManagers.yarn, '/**/file.lock']
229+
])(
230+
'getCacheDirectoriesPaths should throw in case of having not directories',
231+
async (packageManagerInfo, cacheDependency) => {
204232
lstatSpy.mockImplementation(arg => ({
205233
isDirectory: () => false
206234
}));
@@ -248,9 +276,8 @@ two
248276
}
249277
);
250278

251-
// TODO: by design - glob is not expected to return duplicates so 3 patterns do not collapse to 2
252279
it.each(['1.1.1', '2.2.2'])(
253-
'getCacheDirectoriesPaths yarn v%s should return 3 dirs with globbed cacheDependency expanding to duplicates',
280+
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
254281
async version => {
255282
let dirNo = 1;
256283
getCommandOutputSpy.mockImplementation((command: string) =>
@@ -269,11 +296,7 @@ two
269296
supportedPackageManagers.yarn,
270297
'/tmp/**/file'
271298
);
272-
expect(dirs).toEqual([
273-
`file_${version}_1`,
274-
`file_${version}_2`,
275-
`file_${version}_3`
276-
]);
299+
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_2`]);
277300
}
278301
);
279302

dist/cache-save/index.js

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60434,7 +60434,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6043460434
return (mod && mod.__esModule) ? mod : { "default": mod };
6043560435
};
6043660436
Object.defineProperty(exports, "__esModule", ({ value: true }));
60437-
exports.isCacheFeatureAvailable = exports.isGhes = exports.getCacheDirectoriesPaths = exports.expandCacheDependencyPath = exports.getPackageManagerInfo = exports.getCommandOutputGuarded = exports.getCommandOutput = exports.supportedPackageManagers = exports.yarn2GetCacheFolderCommand = exports.yarn1GetCacheFolderCommand = exports.pnpmGetCacheFolderCommand = exports.npmGetCacheFolderCommand = void 0;
60437+
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;
6043860438
const core = __importStar(__nccwpck_require__(2186));
6043960439
const exec = __importStar(__nccwpck_require__(1514));
6044060440
const cache = __importStar(__nccwpck_require__(7799));
@@ -60462,7 +60462,7 @@ exports.supportedPackageManagers = {
6046260462
lockFilePatterns: ['yarn.lock'],
6046360463
getCacheFolderPath: (projectDir) => __awaiter(void 0, void 0, void 0, function* () {
6046460464
const yarnVersion = yield exports.getCommandOutputGuarded(`yarn --version`, 'Could not retrieve version of yarn', projectDir);
60465-
core.debug(`Consumed yarn version is ${yarnVersion}`);
60465+
core.debug(`Consumed yarn version is ${yarnVersion} (working dir: "${projectDir}")`);
6046660466
const stdOut = yarnVersion.startsWith('1.')
6046760467
? yield exports.getCommandOutput(exports.yarn1GetCacheFolderCommand, projectDir)
6046860468
: yield exports.getCommandOutput(exports.yarn2GetCacheFolderCommand, projectDir);
@@ -60507,10 +60507,27 @@ const getPackageManagerInfo = (packageManager) => __awaiter(void 0, void 0, void
6050760507
}
6050860508
});
6050960509
exports.getPackageManagerInfo = getPackageManagerInfo;
60510+
exports.expandedPatternsMemoized = {};
60511+
/**
60512+
* Wrapper around `glob.create(pattern).glob()` with the memoization
60513+
* @param pattern is expected to be a globed path
60514+
* @return list of files or directories expanded from glob
60515+
*/
6051060516
const globPatternToArray = (pattern) => __awaiter(void 0, void 0, void 0, function* () {
60517+
const memoized = exports.expandedPatternsMemoized[pattern];
60518+
if (memoized)
60519+
return Promise.resolve(memoized);
6051160520
const globber = yield glob.create(pattern);
60512-
return globber.glob();
60521+
const expanded = yield globber.glob();
60522+
exports.expandedPatternsMemoized[pattern] = expanded;
60523+
return expanded;
6051360524
});
60525+
/**
60526+
* Expands (converts) the string input `cache-dependency-path` to list of files' paths
60527+
* First it breaks the input by new lines and then expand glob patterns if any
60528+
* @param cacheDependencyPath - either a single string or multiline string with possible glob patterns
60529+
* @return list of files on which the cache depends
60530+
*/
6051460531
const expandCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {
6051560532
const multilinePaths = cacheDependencyPath
6051660533
.split(/\r?\n/)
@@ -60521,26 +60538,55 @@ const expandCacheDependencyPath = (cacheDependencyPath) => __awaiter(void 0, voi
6052160538
return expandedPaths.length === 0 ? [''] : expandedPaths.flat();
6052260539
});
6052360540
exports.expandCacheDependencyPath = expandCacheDependencyPath;
60524-
const cacheDependencyPathToCacheFolderPath = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {
60525-
const cacheDependencyPathDirectory = path_1.default.dirname(cacheDependencyPath);
60526-
const cacheFolderPath = fs_1.default.existsSync(cacheDependencyPathDirectory) &&
60527-
fs_1.default.lstatSync(cacheDependencyPathDirectory).isDirectory()
60528-
? yield packageManagerInfo.getCacheFolderPath(cacheDependencyPathDirectory)
60529-
: yield packageManagerInfo.getCacheFolderPath();
60530-
core.debug(`${packageManagerInfo.name} path is ${cacheFolderPath} (derived from cache-dependency-path: "${cacheDependencyPath}")`);
60541+
/**
60542+
* Converts dependency file to the directory it resides in and ensures the directory exists
60543+
* @param cacheDependencyPath - file name
60544+
* @return either directory containing file or null
60545+
*/
60546+
const cacheDependencyPathToProjectDirectory = (cacheDependencyPath) => {
60547+
const projectDirectory = path_1.default.dirname(cacheDependencyPath);
60548+
if (fs_1.default.existsSync(projectDirectory) &&
60549+
fs_1.default.lstatSync(projectDirectory).isDirectory()) {
60550+
core.debug(`Project directory "${projectDirectory}" derived from cache-dependency-path: "${cacheDependencyPath}"`);
60551+
return projectDirectory;
60552+
}
60553+
else {
60554+
core.debug(`No project directory found for cache-dependency-path: "${cacheDependencyPath}", will be skipped`);
60555+
return null;
60556+
}
60557+
};
60558+
/**
60559+
* Expands (converts) the string input `cache-dependency-path` to list of directories that
60560+
* may be project roots
60561+
* @param cacheDependencyPath
60562+
* @return list of directories and possible
60563+
*/
60564+
const cacheDependencyPathToProjectsDirectories = (cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {
60565+
const cacheDependenciesPaths = yield exports.expandCacheDependencyPath(cacheDependencyPath);
60566+
const existingDirectories = cacheDependenciesPaths
60567+
.map(cacheDependencyPath => cacheDependencyPathToProjectDirectory(cacheDependencyPath))
60568+
.filter(path => path !== null);
60569+
if (existingDirectories.length === 0)
60570+
throw Error('No existing directories found containing `cache-dependency-path`="${cacheDependencyPath}"');
60571+
// uniq
60572+
return existingDirectories.filter((cachePath, i, result) => cachePath != null && result.indexOf(cachePath) === i);
60573+
});
60574+
const projectDirectoryToCacheFolderPath = (packageManagerInfo, projectDirectory) => __awaiter(void 0, void 0, void 0, function* () {
60575+
const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath(projectDirectory);
60576+
core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`);
6053160577
return cacheFolderPath;
6053260578
});
60533-
const cacheDependenciesPathsToCacheFoldersPaths = (packageManagerInfo, cacheDependenciesPaths) => __awaiter(void 0, void 0, void 0, function* () {
60534-
const cacheFoldersPaths = yield Promise.all(cacheDependenciesPaths.map(cacheDependencyPath => cacheDependencyPathToCacheFolderPath(packageManagerInfo, cacheDependencyPath)));
60579+
const projectDirectoriesToCacheFoldersPaths = (packageManagerInfo, projectDirectories) => __awaiter(void 0, void 0, void 0, function* () {
60580+
const cacheFoldersPaths = yield Promise.all(projectDirectories.map(projectDirectory => projectDirectoryToCacheFolderPath(packageManagerInfo, projectDirectory)));
6053560581
return cacheFoldersPaths.filter((cachePath, i, result) => result.indexOf(cachePath) === i);
6053660582
});
6053760583
const cacheDependencyPathToCacheFoldersPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {
60538-
const cacheDependenciesPaths = yield exports.expandCacheDependencyPath(cacheDependencyPath);
60539-
return cacheDependenciesPathsToCacheFoldersPaths(packageManagerInfo, cacheDependenciesPaths);
60584+
const projectDirectories = yield cacheDependencyPathToProjectsDirectories(cacheDependencyPath);
60585+
return projectDirectoriesToCacheFoldersPaths(packageManagerInfo, projectDirectories);
6054060586
});
6054160587
const cacheFoldersPathsForRoot = (packageManagerInfo) => __awaiter(void 0, void 0, void 0, function* () {
6054260588
const cacheFolderPath = yield packageManagerInfo.getCacheFolderPath();
60543-
core.debug(`${packageManagerInfo.name} path is ${cacheFolderPath}`);
60589+
core.debug(`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the root directory`);
6054460590
return [cacheFolderPath];
6054560591
});
6054660592
const getCacheDirectoriesPaths = (packageManagerInfo, cacheDependencyPath) => __awaiter(void 0, void 0, void 0, function* () {

0 commit comments

Comments
 (0)