Merge branch 'actions:main' into main

This commit is contained in:
Sergey Dolin
2022-04-06 12:51:31 +05:00
committed by GitHub
29 changed files with 14313 additions and 2293 deletions

View File

@ -41,12 +41,17 @@ abstract class CacheDistributor {
restoreKey
);
this.handleMatchResult(matchedKey, primaryKey);
}
public handleMatchResult(matchedKey: string | undefined, primaryKey: string) {
if (matchedKey) {
core.saveState(State.CACHE_MATCHED_KEY, matchedKey);
core.info(`Cache restored from key: ${matchedKey}`);
} else {
core.info(`${this.packageManager} cache is not found`);
}
core.setOutput('cache-hit', matchedKey === primaryKey);
}
}

View File

@ -1,9 +1,11 @@
import PipCache from './pip-cache';
import PipenvCache from './pipenv-cache';
import PoetryCache from './poetry-cache';
export enum PackageManagers {
Pip = 'pip',
Pipenv = 'pipenv'
Pipenv = 'pipenv',
Poetry = 'poetry'
}
export function getCacheDistributor(
@ -16,6 +18,8 @@ export function getCacheDistributor(
return new PipCache(pythonVersion, cacheDependencyPath);
case PackageManagers.Pipenv:
return new PipenvCache(pythonVersion, cacheDependencyPath);
case PackageManagers.Poetry:
return new PoetryCache(pythonVersion, cacheDependencyPath);
default:
throw new Error(`Caching for '${packageManager}' is not supported`);
}

View File

@ -0,0 +1,76 @@
import * as glob from '@actions/glob';
import * as os from 'os';
import * as path from 'path';
import * as exec from '@actions/exec';
import CacheDistributor from './cache-distributor';
class PoetryCache extends CacheDistributor {
constructor(
private pythonVersion: string,
protected patterns: string = '**/poetry.lock'
) {
super('poetry', patterns);
}
protected async getCacheGlobalDirectories() {
const poetryConfig = await this.getPoetryConfiguration();
const cacheDir = poetryConfig['cache-dir'];
const virtualenvsPath = poetryConfig['virtualenvs.path'].replace(
'{cache-dir}',
cacheDir
);
const paths = [virtualenvsPath];
if (poetryConfig['virtualenvs.in-project'] === true) {
paths.push(path.join(process.cwd(), '.venv'));
}
return paths;
}
protected async computeKeys() {
const hash = await glob.hashFiles(this.patterns);
const primaryKey = `${this.CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-python-${this.pythonVersion}-${this.packageManager}-${hash}`;
const restoreKey = undefined;
return {
primaryKey,
restoreKey
};
}
private async getPoetryConfiguration() {
const {stdout, stderr, exitCode} = await exec.getExecOutput('poetry', [
'config',
'--list'
]);
if (exitCode && stderr) {
throw new Error(
'Could not get cache folder path for poetry package manager'
);
}
const lines = stdout.trim().split('\n');
const config: any = {};
for (let line of lines) {
line = line.replace(/#.*$/, '');
const [key, value] = line.split('=').map(part => part.trim());
config[key] = JSON.parse(value);
}
return config as {
'cache-dir': string;
'virtualenvs.in-project': boolean;
'virtualenvs.path': string;
};
}
}
export default PoetryCache;

View File

@ -52,6 +52,7 @@ export async function findPyPyVersion(
core.exportVariable('pythonLocation', pythonLocation);
core.addPath(pythonLocation);
core.addPath(_binDir);
core.setOutput('python-version', 'pypy' + resolvedPyPyVersion.trim());
return {resolvedPyPyVersion, resolvedPythonVersion};
}

View File

@ -30,52 +30,7 @@ function binDir(installDir: string): string {
}
}
// Note on the tool cache layout for PyPy:
// PyPy has its own versioning scheme that doesn't follow the Python versioning scheme.
// A particular version of PyPy may contain one or more versions of the Python interpreter.
// For example, PyPy 7.0 contains Python 2.7, 3.5, and 3.6-alpha.
// We only care about the Python version, so we don't use the PyPy version for the tool cache.
function usePyPy(
majorVersion: '2' | '3.6',
architecture: string
): InstalledVersion {
const findPyPy = tc.find.bind(undefined, 'PyPy', majorVersion);
let installDir: string | null = findPyPy(architecture);
if (!installDir && IS_WINDOWS) {
// PyPy only precompiles binaries for x86, but the architecture parameter defaults to x64.
// On our Windows virtual environments, we only install an x86 version.
// Fall back to x86.
installDir = findPyPy('x86');
}
if (!installDir) {
// PyPy not installed in $(Agent.ToolsDirectory)
throw new Error(`PyPy ${majorVersion} not found`);
}
// For PyPy, Windows uses 'bin', not 'Scripts'.
const _binDir = path.join(installDir, 'bin');
// On Linux and macOS, the Python interpreter is in 'bin'.
// On Windows, it is in the installation root.
const pythonLocation = IS_WINDOWS ? installDir : _binDir;
core.exportVariable('pythonLocation', pythonLocation);
core.addPath(installDir);
core.addPath(_binDir);
// Starting from PyPy 7.3.1, the folder that is used for pip and anything that pip installs should be "Scripts" on Windows.
if (IS_WINDOWS) {
core.addPath(path.join(installDir, 'Scripts'));
}
const impl = 'pypy' + majorVersion.toString();
core.setOutput('python-version', impl);
return {impl: impl, version: versionFromPath(installDir)};
}
async function useCpythonVersion(
export async function useCpythonVersion(
version: string,
architecture: string
): Promise<InstalledVersion> {
@ -186,18 +141,3 @@ export function pythonVersionToSemantic(versionSpec: string) {
const prereleaseVersion = /(\d+\.\d+\.\d+)((?:a|b|rc)\d*)/g;
return versionSpec.replace(prereleaseVersion, '$1-$2');
}
export async function findPythonVersion(
version: string,
architecture: string
): Promise<InstalledVersion> {
switch (version.toUpperCase()) {
case 'PYPY2':
return usePyPy('2', architecture);
case 'PYPY3':
// keep pypy3 pointing to 3.6 for backward compatibility
return usePyPy('3.6', architecture);
default:
return await useCpythonVersion(version, architecture);
}
}

View File

@ -4,16 +4,13 @@ import * as finderPyPy from './find-pypy';
import * as path from 'path';
import * as os from 'os';
import {getCacheDistributor} from './cache-distributions/cache-factory';
import {isGhes} from './utils';
import {isCacheFeatureAvailable} from './utils';
function isPyPyVersion(versionSpec: string) {
return versionSpec.startsWith('pypy-');
}
async function cacheDependencies(cache: string, pythonVersion: string) {
if (isGhes()) {
throw new Error('Caching is not supported on GHES');
}
const cacheDependencyPath =
core.getInput('cache-dependency-path') || undefined;
const cacheDistributor = getCacheDistributor(
@ -47,13 +44,13 @@ async function run() {
`Successfully setup PyPy ${installed.resolvedPyPyVersion} with Python (${installed.resolvedPythonVersion})`
);
} else {
const installed = await finder.findPythonVersion(version, arch);
const installed = await finder.useCpythonVersion(version, arch);
pythonVersion = installed.version;
core.info(`Successfully setup ${installed.impl} (${pythonVersion})`);
}
const cache = core.getInput('cache');
if (cache) {
if (cache && isCacheFeatureAvailable()) {
await cacheDependencies(cache, pythonVersion);
}
}

View File

@ -1,3 +1,5 @@
import * as cache from '@actions/cache';
import * as core from '@actions/core';
import fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
@ -99,3 +101,21 @@ export function isGhes(): boolean {
);
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
export function isCacheFeatureAvailable(): boolean {
if (!cache.isFeatureAvailable()) {
if (isGhes()) {
throw new Error(
'Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'
);
} else {
core.warning(
'The runner was not able to contact the cache service. Caching will be skipped'
);
}
return false;
}
return true;
}