From 7b9ef6fc5a7a74dfb7c0924eb42615d7af1e4de5 Mon Sep 17 00:00:00 2001 From: Evgenii Korolevskii <102794661+e-korolevskii@users.noreply.github.com> Date: Mon, 20 Feb 2023 13:36:57 +0100 Subject: [PATCH] Add another pip default dependency file for cache hash (#604) --- README.md | 2 +- __tests__/cache-restore.test.ts | 43 +++++++++++++++++--- dist/cache-save/index.js | 23 +++++++++-- dist/setup/index.js | 28 +++++++++++-- src/cache-distributions/cache-distributor.ts | 11 +++-- src/cache-distributions/constants.ts | 2 + src/cache-distributions/pip-cache.ts | 7 +++- 7 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 src/cache-distributions/constants.ts diff --git a/README.md b/README.md index d6e2e98..3519cfb 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Using `architecture` input it is possible to specify the required Python or PyPy The action has built-in functionality for caching and restoring dependencies. It uses [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under the hood for caching dependencies but requires less configuration settings. Supported package managers are `pip`, `pipenv` and `poetry`. The `cache` input is optional, and caching is turned off by default. -The action defaults to searching for a dependency file (`requirements.txt` for pip, `Pipfile.lock` for pipenv or `poetry.lock` for poetry) in the repository, and uses its hash as a part of the cache key. Input `cache-dependency-path` is used for cases when multiple dependency files are used, they are located in different subdirectories or different files for the hash that want to be used. +The action defaults to searching for a dependency file (`requirements.txt` or `pyproject.toml` for pip, `Pipfile.lock` for pipenv or `poetry.lock` for poetry) in the repository, and uses its hash as a part of the cache key. Input `cache-dependency-path` is used for cases when multiple dependency files are used, they are located in different subdirectories or different files for the hash that want to be used. - For `pip`, the action will cache the global cache directory - For `pipenv`, the action will cache virtualenv directory diff --git a/__tests__/cache-restore.test.ts b/__tests__/cache-restore.test.ts index d6ed832..2503427 100644 --- a/__tests__/cache-restore.test.ts +++ b/__tests__/cache-restore.test.ts @@ -5,7 +5,7 @@ import * as exec from '@actions/exec'; import * as io from '@actions/io'; import {getCacheDistributor} from '../src/cache-distributions/cache-factory'; import {State} from '../src/cache-distributions/cache-distributor'; -import * as utils from './../src/utils'; +import * as constants from '../src/cache-distributions/constants'; describe('restore-cache', () => { const pipFileLockHash = @@ -196,11 +196,7 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py 30000 ); - it.each([ - ['pip', '3.8.12', 'requirements-linux.txt', 'requirements-linux.txt'], - ['pip', '3.8.12', 'requirements.txt', 'requirements.txt'], - ['pipenv', '3.9.12', 'requirements.txt', 'requirements.txt'] - ])( + it.each([['pipenv', '3.9.12', 'requirements.txt', 'requirements.txt']])( 'Should throw an error because dependency file is not found', async ( packageManager, @@ -213,6 +209,7 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py pythonVersion, dependencyFile ); + await expect(cacheDistributor.restoreCache()).rejects.toThrowError( `No file in ${process.cwd()} matched to [${cacheDependencyPath .split('\n') @@ -220,6 +217,40 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py ); } ); + + it.each([ + ['pip', '3.8.12', 'requirements-linux.txt'], + ['pip', '3.8.12', 'requirements.txt'] + ])( + 'Shouldn`t throw an error as there is a default file `pyproject.toml` to use when requirements.txt is not specified', + async (packageManager, pythonVersion, dependencyFile) => { + const cacheDistributor = getCacheDistributor( + packageManager, + pythonVersion, + dependencyFile + ); + await expect(cacheDistributor.restoreCache()).resolves; + } + ); + + it.each([ + ['pip', '3.8.12', 'requirements-linux.txt'], + ['pip', '3.8.12', 'requirements.txt'] + ])( + 'Should throw an error as there is no default file `pyproject.toml` to use when requirements.txt is not specified', + async (packageManager, pythonVersion, dependencyFile) => { + jest.mock('../src/cache-distributions/constants', () => ({ + CACHE_DEPENDENCY_BACKUP_PATH: '**/pyprojecttest.toml' + })); + + const cacheDistributor = getCacheDistributor( + packageManager, + pythonVersion, + dependencyFile + ); + await expect(cacheDistributor.restoreCache()).resolves; + } + ); }); describe('Dependencies changed', () => { diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index 09a9fc6..bef6790 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -59699,6 +59699,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.State = void 0; const cache = __importStar(__nccwpck_require__(7799)); const core = __importStar(__nccwpck_require__(2186)); +const constants_1 = __nccwpck_require__(8248); var State; (function (State) { State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key"; @@ -59718,9 +59719,12 @@ class CacheDistributor { return __awaiter(this, void 0, void 0, function* () { const { primaryKey, restoreKey } = yield this.computeKeys(); if (primaryKey.endsWith('-')) { - throw new Error(`No file in ${process.cwd()} matched to [${this.cacheDependencyPath - .split('\n') - .join(',')}], make sure you have checked out the target repository`); + const file = this.packageManager === 'pip' + ? `${this.cacheDependencyPath + .split('\n') + .join(',')} or ${constants_1.CACHE_DEPENDENCY_BACKUP_PATH}` + : this.cacheDependencyPath.split('\n').join(','); + throw new Error(`No file in ${process.cwd()} matched to [${file}], make sure you have checked out the target repository`); } const cachePath = yield this.getCacheGlobalDirectories(); core.saveState(State.CACHE_PATHS, cachePath); @@ -59744,6 +59748,19 @@ class CacheDistributor { exports["default"] = CacheDistributor; +/***/ }), + +/***/ 8248: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.CACHE_DEPENDENCY_BACKUP_PATH = void 0; +const CACHE_DEPENDENCY_BACKUP_PATH = '**/pyproject.toml'; +exports.CACHE_DEPENDENCY_BACKUP_PATH = CACHE_DEPENDENCY_BACKUP_PATH; + + /***/ }), /***/ 4553: diff --git a/dist/setup/index.js b/dist/setup/index.js index cc4cb8d..b512471 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -65775,6 +65775,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.State = void 0; const cache = __importStar(__nccwpck_require__(7799)); const core = __importStar(__nccwpck_require__(2186)); +const constants_1 = __nccwpck_require__(8248); var State; (function (State) { State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key"; @@ -65794,9 +65795,12 @@ class CacheDistributor { return __awaiter(this, void 0, void 0, function* () { const { primaryKey, restoreKey } = yield this.computeKeys(); if (primaryKey.endsWith('-')) { - throw new Error(`No file in ${process.cwd()} matched to [${this.cacheDependencyPath - .split('\n') - .join(',')}], make sure you have checked out the target repository`); + const file = this.packageManager === 'pip' + ? `${this.cacheDependencyPath + .split('\n') + .join(',')} or ${constants_1.CACHE_DEPENDENCY_BACKUP_PATH}` + : this.cacheDependencyPath.split('\n').join(','); + throw new Error(`No file in ${process.cwd()} matched to [${file}], make sure you have checked out the target repository`); } const cachePath = yield this.getCacheGlobalDirectories(); core.saveState(State.CACHE_PATHS, cachePath); @@ -65856,6 +65860,19 @@ function getCacheDistributor(packageManager, pythonVersion, cacheDependencyPath) exports.getCacheDistributor = getCacheDistributor; +/***/ }), + +/***/ 8248: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.CACHE_DEPENDENCY_BACKUP_PATH = void 0; +const CACHE_DEPENDENCY_BACKUP_PATH = '**/pyproject.toml'; +exports.CACHE_DEPENDENCY_BACKUP_PATH = CACHE_DEPENDENCY_BACKUP_PATH; + + /***/ }), /***/ 5546: @@ -65904,10 +65921,12 @@ const path = __importStar(__nccwpck_require__(1017)); const os_1 = __importDefault(__nccwpck_require__(2037)); const cache_distributor_1 = __importDefault(__nccwpck_require__(8953)); const utils_1 = __nccwpck_require__(1314); +const constants_1 = __nccwpck_require__(8248); class PipCache extends cache_distributor_1.default { constructor(pythonVersion, cacheDependencyPath = '**/requirements.txt') { super('pip', cacheDependencyPath); this.pythonVersion = pythonVersion; + this.cacheDependencyBackupPath = constants_1.CACHE_DEPENDENCY_BACKUP_PATH; } getCacheGlobalDirectories() { return __awaiter(this, void 0, void 0, function* () { @@ -65943,7 +65962,8 @@ class PipCache extends cache_distributor_1.default { } computeKeys() { return __awaiter(this, void 0, void 0, function* () { - const hash = yield glob.hashFiles(this.cacheDependencyPath); + const hash = (yield glob.hashFiles(this.cacheDependencyPath)) || + (yield glob.hashFiles(this.cacheDependencyBackupPath)); let primaryKey = ''; let restoreKey = ''; if (utils_1.IS_LINUX) { diff --git a/src/cache-distributions/cache-distributor.ts b/src/cache-distributions/cache-distributor.ts index 2e46c96..29fba0c 100644 --- a/src/cache-distributions/cache-distributor.ts +++ b/src/cache-distributions/cache-distributor.ts @@ -1,5 +1,6 @@ import * as cache from '@actions/cache'; import * as core from '@actions/core'; +import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants'; export enum State { STATE_CACHE_PRIMARY_KEY = 'cache-primary-key', @@ -24,10 +25,14 @@ abstract class CacheDistributor { public async restoreCache() { const {primaryKey, restoreKey} = await this.computeKeys(); if (primaryKey.endsWith('-')) { + const file = + this.packageManager === 'pip' + ? `${this.cacheDependencyPath + .split('\n') + .join(',')} or ${CACHE_DEPENDENCY_BACKUP_PATH}` + : this.cacheDependencyPath.split('\n').join(','); throw new Error( - `No file in ${process.cwd()} matched to [${this.cacheDependencyPath - .split('\n') - .join(',')}], make sure you have checked out the target repository` + `No file in ${process.cwd()} matched to [${file}], make sure you have checked out the target repository` ); } diff --git a/src/cache-distributions/constants.ts b/src/cache-distributions/constants.ts new file mode 100644 index 0000000..74edfd9 --- /dev/null +++ b/src/cache-distributions/constants.ts @@ -0,0 +1,2 @@ +const CACHE_DEPENDENCY_BACKUP_PATH: string = '**/pyproject.toml'; +export {CACHE_DEPENDENCY_BACKUP_PATH}; diff --git a/src/cache-distributions/pip-cache.ts b/src/cache-distributions/pip-cache.ts index 25b29c6..c29d4cd 100644 --- a/src/cache-distributions/pip-cache.ts +++ b/src/cache-distributions/pip-cache.ts @@ -8,8 +8,11 @@ import os from 'os'; import CacheDistributor from './cache-distributor'; import {getLinuxInfo, IS_LINUX, IS_WINDOWS} from '../utils'; +import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants'; class PipCache extends CacheDistributor { + private cacheDependencyBackupPath: string = CACHE_DEPENDENCY_BACKUP_PATH; + constructor( private pythonVersion: string, cacheDependencyPath: string = '**/requirements.txt' @@ -56,7 +59,9 @@ class PipCache extends CacheDistributor { } protected async computeKeys() { - const hash = await glob.hashFiles(this.cacheDependencyPath); + const hash = + (await glob.hashFiles(this.cacheDependencyPath)) || + (await glob.hashFiles(this.cacheDependencyBackupPath)); let primaryKey = ''; let restoreKey = '';