Add another pip default dependency file for cache hash (#604)

This commit is contained in:
Evgenii Korolevskii 2023-02-20 13:36:57 +01:00 committed by GitHub
parent 869e769ec8
commit 7b9ef6fc5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 98 additions and 18 deletions

View File

@ -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 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 `pip`, the action will cache the global cache directory
- For `pipenv`, the action will cache virtualenv directory - For `pipenv`, the action will cache virtualenv directory

View File

@ -5,7 +5,7 @@ import * as exec from '@actions/exec';
import * as io from '@actions/io'; import * as io from '@actions/io';
import {getCacheDistributor} from '../src/cache-distributions/cache-factory'; import {getCacheDistributor} from '../src/cache-distributions/cache-factory';
import {State} from '../src/cache-distributions/cache-distributor'; 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', () => { describe('restore-cache', () => {
const pipFileLockHash = const pipFileLockHash =
@ -196,11 +196,7 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
30000 30000
); );
it.each([ it.each([['pipenv', '3.9.12', 'requirements.txt', 'requirements.txt']])(
['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']
])(
'Should throw an error because dependency file is not found', 'Should throw an error because dependency file is not found',
async ( async (
packageManager, packageManager,
@ -213,6 +209,7 @@ virtualenvs.path = "{cache-dir}/virtualenvs" # /Users/patrick/Library/Caches/py
pythonVersion, pythonVersion,
dependencyFile dependencyFile
); );
await expect(cacheDistributor.restoreCache()).rejects.toThrowError( await expect(cacheDistributor.restoreCache()).rejects.toThrowError(
`No file in ${process.cwd()} matched to [${cacheDependencyPath `No file in ${process.cwd()} matched to [${cacheDependencyPath
.split('\n') .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', () => { describe('Dependencies changed', () => {

View File

@ -59699,6 +59699,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.State = void 0; exports.State = void 0;
const cache = __importStar(__nccwpck_require__(7799)); const cache = __importStar(__nccwpck_require__(7799));
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const constants_1 = __nccwpck_require__(8248);
var State; var State;
(function (State) { (function (State) {
State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key"; State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key";
@ -59718,9 +59719,12 @@ class CacheDistributor {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const { primaryKey, restoreKey } = yield this.computeKeys(); const { primaryKey, restoreKey } = yield this.computeKeys();
if (primaryKey.endsWith('-')) { if (primaryKey.endsWith('-')) {
throw new Error(`No file in ${process.cwd()} matched to [${this.cacheDependencyPath const file = this.packageManager === 'pip'
.split('\n') ? `${this.cacheDependencyPath
.join(',')}], make sure you have checked out the target repository`); .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(); const cachePath = yield this.getCacheGlobalDirectories();
core.saveState(State.CACHE_PATHS, cachePath); core.saveState(State.CACHE_PATHS, cachePath);
@ -59744,6 +59748,19 @@ class CacheDistributor {
exports["default"] = 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: /***/ 4553:

28
dist/setup/index.js vendored
View File

@ -65775,6 +65775,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.State = void 0; exports.State = void 0;
const cache = __importStar(__nccwpck_require__(7799)); const cache = __importStar(__nccwpck_require__(7799));
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const constants_1 = __nccwpck_require__(8248);
var State; var State;
(function (State) { (function (State) {
State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key"; State["STATE_CACHE_PRIMARY_KEY"] = "cache-primary-key";
@ -65794,9 +65795,12 @@ class CacheDistributor {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const { primaryKey, restoreKey } = yield this.computeKeys(); const { primaryKey, restoreKey } = yield this.computeKeys();
if (primaryKey.endsWith('-')) { if (primaryKey.endsWith('-')) {
throw new Error(`No file in ${process.cwd()} matched to [${this.cacheDependencyPath const file = this.packageManager === 'pip'
.split('\n') ? `${this.cacheDependencyPath
.join(',')}], make sure you have checked out the target repository`); .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(); const cachePath = yield this.getCacheGlobalDirectories();
core.saveState(State.CACHE_PATHS, cachePath); core.saveState(State.CACHE_PATHS, cachePath);
@ -65856,6 +65860,19 @@ function getCacheDistributor(packageManager, pythonVersion, cacheDependencyPath)
exports.getCacheDistributor = getCacheDistributor; 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: /***/ 5546:
@ -65904,10 +65921,12 @@ const path = __importStar(__nccwpck_require__(1017));
const os_1 = __importDefault(__nccwpck_require__(2037)); const os_1 = __importDefault(__nccwpck_require__(2037));
const cache_distributor_1 = __importDefault(__nccwpck_require__(8953)); const cache_distributor_1 = __importDefault(__nccwpck_require__(8953));
const utils_1 = __nccwpck_require__(1314); const utils_1 = __nccwpck_require__(1314);
const constants_1 = __nccwpck_require__(8248);
class PipCache extends cache_distributor_1.default { class PipCache extends cache_distributor_1.default {
constructor(pythonVersion, cacheDependencyPath = '**/requirements.txt') { constructor(pythonVersion, cacheDependencyPath = '**/requirements.txt') {
super('pip', cacheDependencyPath); super('pip', cacheDependencyPath);
this.pythonVersion = pythonVersion; this.pythonVersion = pythonVersion;
this.cacheDependencyBackupPath = constants_1.CACHE_DEPENDENCY_BACKUP_PATH;
} }
getCacheGlobalDirectories() { getCacheGlobalDirectories() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -65943,7 +65962,8 @@ class PipCache extends cache_distributor_1.default {
} }
computeKeys() { computeKeys() {
return __awaiter(this, void 0, void 0, function* () { 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 primaryKey = '';
let restoreKey = ''; let restoreKey = '';
if (utils_1.IS_LINUX) { if (utils_1.IS_LINUX) {

View File

@ -1,5 +1,6 @@
import * as cache from '@actions/cache'; import * as cache from '@actions/cache';
import * as core from '@actions/core'; import * as core from '@actions/core';
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
export enum State { export enum State {
STATE_CACHE_PRIMARY_KEY = 'cache-primary-key', STATE_CACHE_PRIMARY_KEY = 'cache-primary-key',
@ -24,10 +25,14 @@ abstract class CacheDistributor {
public async restoreCache() { public async restoreCache() {
const {primaryKey, restoreKey} = await this.computeKeys(); const {primaryKey, restoreKey} = await this.computeKeys();
if (primaryKey.endsWith('-')) { 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( throw new Error(
`No file in ${process.cwd()} matched to [${this.cacheDependencyPath `No file in ${process.cwd()} matched to [${file}], make sure you have checked out the target repository`
.split('\n')
.join(',')}], make sure you have checked out the target repository`
); );
} }

View File

@ -0,0 +1,2 @@
const CACHE_DEPENDENCY_BACKUP_PATH: string = '**/pyproject.toml';
export {CACHE_DEPENDENCY_BACKUP_PATH};

View File

@ -8,8 +8,11 @@ import os from 'os';
import CacheDistributor from './cache-distributor'; import CacheDistributor from './cache-distributor';
import {getLinuxInfo, IS_LINUX, IS_WINDOWS} from '../utils'; import {getLinuxInfo, IS_LINUX, IS_WINDOWS} from '../utils';
import {CACHE_DEPENDENCY_BACKUP_PATH} from './constants';
class PipCache extends CacheDistributor { class PipCache extends CacheDistributor {
private cacheDependencyBackupPath: string = CACHE_DEPENDENCY_BACKUP_PATH;
constructor( constructor(
private pythonVersion: string, private pythonVersion: string,
cacheDependencyPath: string = '**/requirements.txt' cacheDependencyPath: string = '**/requirements.txt'
@ -56,7 +59,9 @@ class PipCache extends CacheDistributor {
} }
protected async computeKeys() { 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 primaryKey = '';
let restoreKey = ''; let restoreKey = '';