mirror of
https://github.com/actions/setup-node.git
synced 2025-06-30 14:43:48 +00:00
Handle globs in cacheDependencyPath
This commit is contained in:
parent
2b97a8fb23
commit
914a8e9bcc
11 changed files with 1855 additions and 293 deletions
|
@ -6,14 +6,14 @@ import fs from 'fs';
|
|||
|
||||
import {State} from './constants';
|
||||
import {
|
||||
getCacheDirectoryPath,
|
||||
getCacheDirectoriesPaths,
|
||||
getPackageManagerInfo,
|
||||
PackageManagerInfo
|
||||
} from './cache-utils';
|
||||
|
||||
export const restoreCache = async (
|
||||
packageManager: string,
|
||||
cacheDependencyPath?: string
|
||||
cacheDependencyPath: string
|
||||
) => {
|
||||
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
||||
if (!packageManagerInfo) {
|
||||
|
@ -21,9 +21,9 @@ export const restoreCache = async (
|
|||
}
|
||||
const platform = process.env.RUNNER_OS;
|
||||
|
||||
const cachePath = await getCacheDirectoryPath(
|
||||
const cachePaths = await getCacheDirectoriesPaths(
|
||||
packageManagerInfo,
|
||||
packageManager
|
||||
cacheDependencyPath
|
||||
);
|
||||
const lockFilePath = cacheDependencyPath
|
||||
? cacheDependencyPath
|
||||
|
@ -41,7 +41,7 @@ export const restoreCache = async (
|
|||
|
||||
core.saveState(State.CachePrimaryKey, primaryKey);
|
||||
|
||||
const cacheKey = await cache.restoreCache([cachePath], primaryKey);
|
||||
const cacheKey = await cache.restoreCache(cachePaths, primaryKey);
|
||||
core.setOutput('cache-hit', Boolean(cacheKey));
|
||||
|
||||
if (!cacheKey) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as core from '@actions/core';
|
|||
import * as cache from '@actions/cache';
|
||||
import fs from 'fs';
|
||||
import {State} from './constants';
|
||||
import {getCacheDirectoryPath, getPackageManagerInfo} from './cache-utils';
|
||||
import {getCacheDirectoriesPaths, 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
|
||||
|
@ -31,14 +31,17 @@ const cachePackages = async (packageManager: string) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const cachePath = await getCacheDirectoryPath(
|
||||
// TODO: core.getInput has a bug - it can return undefined despite its definition
|
||||
// export declare function getInput(name: string, options?: InputOptions): string;
|
||||
const cacheDependencyPath = core.getInput('cache-dependency-path') || '';
|
||||
const cachePaths = await getCacheDirectoriesPaths(
|
||||
packageManagerInfo,
|
||||
packageManager
|
||||
cacheDependencyPath
|
||||
);
|
||||
|
||||
if (!fs.existsSync(cachePath)) {
|
||||
if (cachePaths.length === 0) {
|
||||
throw new Error(
|
||||
`Cache folder path is retrieved for ${packageManager} but doesn't exist on disk: ${cachePath}`
|
||||
`Cache folder paths are not retrieved for ${packageManager} with cache-dependency-path = ${cacheDependencyPath}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -49,7 +52,7 @@ const cachePackages = async (packageManager: string) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const cacheId = await cache.saveCache([cachePath], primaryKey);
|
||||
const cacheId = await cache.saveCache(cachePaths, primaryKey);
|
||||
if (cacheId == -1) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,45 +1,80 @@
|
|||
import * as core from '@actions/core';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as cache from '@actions/cache';
|
||||
import * as glob from '@actions/glob';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
type SupportedPackageManagers = {
|
||||
[prop: string]: PackageManagerInfo;
|
||||
};
|
||||
|
||||
export interface PackageManagerInfo {
|
||||
name: string;
|
||||
lockFilePatterns: Array<string>;
|
||||
getCacheFolderCommand: string;
|
||||
getCacheFolderPath: (projectDir?: string) => Promise<string>;
|
||||
}
|
||||
|
||||
interface SupportedPackageManagers {
|
||||
npm: PackageManagerInfo;
|
||||
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'],
|
||||
getCacheFolderCommand: 'npm config get cache'
|
||||
getCacheFolderPath: () =>
|
||||
getCommandOutputGuarded(
|
||||
npmGetCacheFolderCommand,
|
||||
'Could not get npm cache folder path'
|
||||
)
|
||||
},
|
||||
pnpm: {
|
||||
name: 'pnpm',
|
||||
lockFilePatterns: ['pnpm-lock.yaml'],
|
||||
getCacheFolderCommand: 'pnpm store path --silent'
|
||||
getCacheFolderPath: () =>
|
||||
getCommandOutputGuarded(
|
||||
pnpmGetCacheFolderCommand,
|
||||
'Could not get pnpm cache folder path'
|
||||
)
|
||||
},
|
||||
yarn1: {
|
||||
yarn: {
|
||||
name: 'yarn',
|
||||
lockFilePatterns: ['yarn.lock'],
|
||||
getCacheFolderCommand: 'yarn cache dir'
|
||||
},
|
||||
yarn2: {
|
||||
lockFilePatterns: ['yarn.lock'],
|
||||
getCacheFolderCommand: 'yarn config get cacheFolder'
|
||||
getCacheFolderPath: async projectDir => {
|
||||
const yarnVersion = await getCommandOutputGuarded(
|
||||
`yarn --version`,
|
||||
'Could not retrieve version of yarn',
|
||||
projectDir
|
||||
);
|
||||
|
||||
core.debug(`Consumed yarn version is ${yarnVersion}`);
|
||||
|
||||
const stdOut = yarnVersion.startsWith('1.')
|
||||
? await getCommandOutput(yarn1GetCacheFolderCommand, projectDir)
|
||||
: await getCommandOutput(yarn2GetCacheFolderCommand, projectDir);
|
||||
|
||||
if (!stdOut) {
|
||||
throw new Error(
|
||||
`Could not get yarn cache folder path for ${projectDir}`
|
||||
);
|
||||
}
|
||||
return stdOut;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getCommandOutput = async (
|
||||
toolCommand: string,
|
||||
cwd: string | null
|
||||
) => {
|
||||
cwd?: string
|
||||
): Promise<string> => {
|
||||
let {stdout, stderr, exitCode} = await exec.getExecOutput(
|
||||
toolCommand,
|
||||
undefined,
|
||||
{ignoreReturnCode: true, ...(cwd !== null && {cwd})}
|
||||
{ignoreReturnCode: true, ...(cwd && {cwd})}
|
||||
);
|
||||
|
||||
if (exitCode) {
|
||||
|
@ -52,41 +87,15 @@ export const getCommandOutput = async (
|
|||
return stdout.trim();
|
||||
};
|
||||
|
||||
export const getPackageManagerWorkingDir = (): string | null => {
|
||||
const cache = core.getInput('cache');
|
||||
if (cache !== 'yarn') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cacheDependencyPath = core.getInput('cache-dependency-path');
|
||||
if (!cacheDependencyPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const wd = path.dirname(cacheDependencyPath);
|
||||
|
||||
if (fs.existsSync(wd) && fs.lstatSync(wd).isDirectory()) {
|
||||
return wd;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getPackageManagerCommandOutput = (command: string) =>
|
||||
getCommandOutput(command, getPackageManagerWorkingDir());
|
||||
|
||||
export const getPackageManagerVersion = async (
|
||||
packageManager: string,
|
||||
command: string
|
||||
) => {
|
||||
const stdOut = await getPackageManagerCommandOutput(
|
||||
`${packageManager} ${command}`
|
||||
);
|
||||
|
||||
export const getCommandOutputGuarded = async (
|
||||
toolCommand: string,
|
||||
error: string,
|
||||
cwd?: string
|
||||
): Promise<string> => {
|
||||
const stdOut = getCommandOutput(toolCommand, cwd);
|
||||
if (!stdOut) {
|
||||
throw new Error(`Could not retrieve version of ${packageManager}`);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
return stdOut;
|
||||
};
|
||||
|
||||
|
@ -96,36 +105,99 @@ export const getPackageManagerInfo = async (packageManager: string) => {
|
|||
} else if (packageManager === 'pnpm') {
|
||||
return supportedPackageManagers.pnpm;
|
||||
} else if (packageManager === 'yarn') {
|
||||
const yarnVersion = await getPackageManagerVersion('yarn', '--version');
|
||||
|
||||
core.debug(`Consumed yarn version is ${yarnVersion}`);
|
||||
|
||||
if (yarnVersion.startsWith('1.')) {
|
||||
return supportedPackageManagers.yarn1;
|
||||
} else {
|
||||
return supportedPackageManagers.yarn2;
|
||||
}
|
||||
return supportedPackageManagers.yarn;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getCacheDirectoryPath = async (
|
||||
const globPatternToArray = async (pattern: string): Promise<string[]> => {
|
||||
const globber = await glob.create(pattern);
|
||||
return globber.glob();
|
||||
};
|
||||
|
||||
export const expandCacheDependencyPath = async (
|
||||
cacheDependencyPath: string
|
||||
): Promise<string[]> => {
|
||||
const multilinePaths = cacheDependencyPath
|
||||
.split(/\r?\n/)
|
||||
.map(path => path.trim())
|
||||
.filter(path => Boolean(path));
|
||||
const expandedPathsPromises: Promise<string[]>[] = multilinePaths.map(path =>
|
||||
path.includes('*') ? globPatternToArray(path) : Promise.resolve([path])
|
||||
);
|
||||
const expandedPaths: string[][] = await Promise.all(expandedPathsPromises);
|
||||
return expandedPaths.length === 0 ? [''] : expandedPaths.flat();
|
||||
};
|
||||
|
||||
const cacheDependencyPathToCacheFolderPath = async (
|
||||
packageManagerInfo: PackageManagerInfo,
|
||||
packageManager: string
|
||||
) => {
|
||||
const stdOut = await getPackageManagerCommandOutput(
|
||||
packageManagerInfo.getCacheFolderCommand
|
||||
cacheDependencyPath: string
|
||||
): Promise<string> => {
|
||||
const cacheDependencyPathDirectory = path.dirname(cacheDependencyPath);
|
||||
const cacheFolderPath =
|
||||
fs.existsSync(cacheDependencyPathDirectory) &&
|
||||
fs.lstatSync(cacheDependencyPathDirectory).isDirectory()
|
||||
? await packageManagerInfo.getCacheFolderPath(
|
||||
cacheDependencyPathDirectory
|
||||
)
|
||||
: await packageManagerInfo.getCacheFolderPath();
|
||||
|
||||
core.debug(
|
||||
`${packageManagerInfo.name} path is ${cacheFolderPath} (derived from cache-dependency-path: "${cacheDependencyPath}")`
|
||||
);
|
||||
|
||||
if (!stdOut) {
|
||||
throw new Error(`Could not get cache folder path for ${packageManager}`);
|
||||
}
|
||||
|
||||
core.debug(`${packageManager} path is ${stdOut}`);
|
||||
|
||||
return stdOut.trim();
|
||||
return cacheFolderPath;
|
||||
};
|
||||
const cacheDependenciesPathsToCacheFoldersPaths = async (
|
||||
packageManagerInfo: PackageManagerInfo,
|
||||
cacheDependenciesPaths: string[]
|
||||
): Promise<string[]> => {
|
||||
const cacheFoldersPaths = await Promise.all(
|
||||
cacheDependenciesPaths.map(cacheDependencyPath =>
|
||||
cacheDependencyPathToCacheFolderPath(
|
||||
packageManagerInfo,
|
||||
cacheDependencyPath
|
||||
)
|
||||
)
|
||||
);
|
||||
return cacheFoldersPaths.filter(
|
||||
(cachePath, i, result) => result.indexOf(cachePath) === i
|
||||
);
|
||||
};
|
||||
|
||||
const cacheDependencyPathToCacheFoldersPaths = async (
|
||||
packageManagerInfo: PackageManagerInfo,
|
||||
cacheDependencyPath: string
|
||||
): Promise<string[]> => {
|
||||
const cacheDependenciesPaths = await expandCacheDependencyPath(
|
||||
cacheDependencyPath
|
||||
);
|
||||
return cacheDependenciesPathsToCacheFoldersPaths(
|
||||
packageManagerInfo,
|
||||
cacheDependenciesPaths
|
||||
);
|
||||
};
|
||||
|
||||
const cacheFoldersPathsForRoot = async (
|
||||
packageManagerInfo: PackageManagerInfo
|
||||
): Promise<string[]> => {
|
||||
const cacheFolderPath = await packageManagerInfo.getCacheFolderPath();
|
||||
core.debug(`${packageManagerInfo.name} path is ${cacheFolderPath}`);
|
||||
return [cacheFolderPath];
|
||||
};
|
||||
|
||||
export const getCacheDirectoriesPaths = async (
|
||||
packageManagerInfo: PackageManagerInfo,
|
||||
cacheDependencyPath: string
|
||||
): Promise<string[]> =>
|
||||
// TODO: multiple directories limited to yarn so far
|
||||
packageManagerInfo === supportedPackageManagers.yarn
|
||||
? cacheDependencyPathToCacheFoldersPaths(
|
||||
packageManagerInfo,
|
||||
cacheDependencyPath
|
||||
)
|
||||
: cacheFoldersPathsForRoot(packageManagerInfo);
|
||||
|
||||
export function isGhes(): boolean {
|
||||
const ghUrl = new URL(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue