Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching on GHES #452

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .licenses/npm/@actions/cache.dep.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 35 additions & 19 deletions __tests__/cache-utils.test.ts
@@ -1,37 +1,28 @@
import * as core from '@actions/core';
import * as cache from '@actions/cache';
import path from 'path';
import * as utils from '../src/cache-utils';
import {PackageManagerInfo} from '../src/cache-utils';
import {PackageManagerInfo, isCacheFeatureAvailable} from '../src/cache-utils';

describe('cache-utils', () => {
const commonPath = '/some/random/path';
const versionYarn1 = '1.2.3';
const versionYarn2 = '2.3.4';

let debugSpy: jest.SpyInstance;
let getCommandOutputSpy: jest.SpyInstance;

function getPackagePath(name: string) {
if (name === utils.supportedPackageManagers.npm.getCacheFolderCommand) {
return `${commonPath}/npm`;
} else if (
name === utils.supportedPackageManagers.pnpm.getCacheFolderCommand
) {
return `${commonPath}/pnpm`;
} else {
if (name === utils.supportedPackageManagers.yarn1.getCacheFolderCommand) {
return `${commonPath}/yarn1`;
} else {
return `${commonPath}/yarn2`;
}
}
}
let isFeatureAvailable: jest.SpyInstance;
let info: jest.SpyInstance;
let warningSpy: jest.SpyInstance;

beforeEach(() => {
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
debugSpy = jest.spyOn(core, 'debug');
debugSpy.mockImplementation(msg => {});

info = jest.spyOn(core, 'info');
warningSpy = jest.spyOn(core, 'warning');

isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');

getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
});

Expand All @@ -51,7 +42,32 @@ describe('cache-utils', () => {
});
});

it('isCacheFeatureAvailable for GHES is false', () => {
isFeatureAvailable.mockImplementation(() => false);
process.env['GITHUB_SERVER_URL'] = 'https://www.test.com';

expect(() => isCacheFeatureAvailable()).toThrowError(
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
);
});

it('isCacheFeatureAvailable for GHES has an interhal error', () => {
isFeatureAvailable.mockImplementation(() => false);
process.env['GITHUB_SERVER_URL'] = '';
isCacheFeatureAvailable();
expect(warningSpy).toHaveBeenCalledWith(
'The runner was not able to contact the cache service. Caching will be skipped'
);
});

it('isCacheFeatureAvailable for GHES is available', () => {
isFeatureAvailable.mockImplementation(() => true);

expect(isCacheFeatureAvailable()).toStrictEqual(true);
});

afterEach(() => {
process.env['GITHUB_SERVER_URL'] = '';
jest.resetAllMocks();
jest.clearAllMocks();
});
Expand Down
48 changes: 48 additions & 0 deletions __tests__/installer.test.ts
Expand Up @@ -2,6 +2,7 @@ import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as im from '../src/installer';
import * as cache from '@actions/cache';
import fs from 'fs';
import cp from 'child_process';
import osm = require('os');
Expand Down Expand Up @@ -36,6 +37,7 @@ describe('setup-node', () => {
let execSpy: jest.SpyInstance;
let authSpy: jest.SpyInstance;
let parseNodeVersionSpy: jest.SpyInstance;
let isCacheActionAvailable: jest.SpyInstance;

beforeEach(() => {
// @actions/core
Expand Down Expand Up @@ -67,6 +69,9 @@ describe('setup-node', () => {
existsSpy = jest.spyOn(fs, 'existsSync');
mkdirpSpy = jest.spyOn(io, 'mkdirP');

// @actions/tool-cache
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');

// disable authentication portion for installer tests
authSpy = jest.spyOn(auth, 'configAuthentication');
authSpy.mockImplementation(() => {});
Expand Down Expand Up @@ -644,6 +649,49 @@ describe('setup-node', () => {
);
});
});

describe('cache on GHES', () => {
it('Should throw an error, because cache is not supported', async () => {
inputs['node-version'] = '12';
inputs['cache'] = 'npm';

inSpy.mockImplementation(name => inputs[name]);

let toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockImplementation(() => toolPath);

// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
process.env['GITHUB_SERVER_URL'] = 'https://www.test.com';
isCacheActionAvailable.mockImplementation(() => false);

await main.run();

expect(cnSpy).toHaveBeenCalledWith(
`::error::Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.${osm.EOL}`
);
});

it('Should throw an internal error', async () => {
inputs['node-version'] = '12';
inputs['cache'] = 'npm';

inSpy.mockImplementation(name => inputs[name]);

let toolPath = path.normalize('/cache/node/12.16.1/x64');
findSpy.mockImplementation(() => toolPath);

// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
process.env['GITHUB_SERVER_URL'] = '';
isCacheActionAvailable.mockImplementation(() => false);

await main.run();

expect(warningSpy).toHaveBeenCalledWith(
'The runner was not able to contact the cache service. Caching will be skipped'
);
});
});

describe('LTS version', () => {
beforeEach(() => {
os.platform = 'linux';
Expand Down
36 changes: 31 additions & 5 deletions dist/cache-save/index.js
Expand Up @@ -3139,10 +3139,7 @@ const options_1 = __webpack_require__(248);
const requestUtils_1 = __webpack_require__(826);
const versionSalt = '1.0';
function getCacheApiUrl(resource) {
// Ideally we just use ACTIONS_CACHE_URL
const baseUrl = (process.env['ACTIONS_CACHE_URL'] ||
process.env['ACTIONS_RUNTIME_URL'] ||
'').replace('pipelines', 'artifactcache');
const baseUrl = process.env['ACTIONS_CACHE_URL'] || '';
if (!baseUrl) {
throw new Error('Cache Service Url not found, unable to restore cache.');
}
Expand Down Expand Up @@ -3811,6 +3808,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const exec = __importStar(__webpack_require__(986));
const cache = __importStar(__webpack_require__(692));
exports.supportedPackageManagers = {
npm: {
lockFilePatterns: ['package-lock.json', 'yarn.lock'],
Expand Down Expand Up @@ -3875,6 +3873,24 @@ exports.getCacheDirectoryPath = (packageManagerInfo, packageManager) => __awaite
core.debug(`${packageManager} path is ${stdOut}`);
return stdOut;
});
function isGhes() {
const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
exports.isGhes = isGhes;
function isCacheFeatureAvailable() {
if (!cache.isFeatureAvailable()) {
if (isGhes()) {
throw new Error('Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if 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;
}
exports.isCacheFeatureAvailable = isCacheFeatureAvailable;


/***/ }),
Expand Down Expand Up @@ -5703,7 +5719,8 @@ function downloadCacheStorageSDK(archiveLocation, archivePath, options) {
//
// If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB
// on 64-bit systems), split the download into multiple segments
const maxSegmentSize = buffer.constants.MAX_LENGTH;
// ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly.
const maxSegmentSize = Math.min(2147483647, buffer.constants.MAX_LENGTH);
const downloadProgress = new DownloadProgress(contentLength);
const fd = fs.openSync(archivePath, 'w');
try {
Expand Down Expand Up @@ -43258,6 +43275,15 @@ function checkKey(key) {
throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`);
}
}
/**
* isFeatureAvailable to check the presence of Actions cache service
*
* @returns boolean return true if Actions cache service feature is available, otherwise false
*/
function isFeatureAvailable() {
return !!process.env['ACTIONS_CACHE_URL'];
}
exports.isFeatureAvailable = isFeatureAvailable;
/**
* Restores cache from keys
*
Expand Down
49 changes: 34 additions & 15 deletions dist/setup/index.js
Expand Up @@ -5410,10 +5410,7 @@ const options_1 = __webpack_require__(161);
const requestUtils_1 = __webpack_require__(246);
const versionSalt = '1.0';
function getCacheApiUrl(resource) {
// Ideally we just use ACTIONS_CACHE_URL
const baseUrl = (process.env['ACTIONS_CACHE_URL'] ||
process.env['ACTIONS_RUNTIME_URL'] ||
'').replace('pipelines', 'artifactcache');
const baseUrl = process.env['ACTIONS_CACHE_URL'] || '';
if (!baseUrl) {
throw new Error('Cache Service Url not found, unable to restore cache.');
}
Expand Down Expand Up @@ -6588,7 +6585,7 @@ const fs_1 = __importDefault(__webpack_require__(747));
const auth = __importStar(__webpack_require__(749));
const path = __importStar(__webpack_require__(622));
const cache_restore_1 = __webpack_require__(409);
const url_1 = __webpack_require__(835);
const cache_utils_1 = __webpack_require__(570);
const os = __webpack_require__(87);
function run() {
return __awaiter(this, void 0, void 0, function* () {
Expand All @@ -6610,7 +6607,7 @@ function run() {
}
if (version) {
let token = core.getInput('token');
let auth = !token || isGhes() ? undefined : `token ${token}`;
let auth = !token || cache_utils_1.isGhes() ? undefined : `token ${token}`;
let stable = (core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
const checkLatest = (core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
yield installer.getNode(version, stable, checkLatest, auth, arch);
Expand All @@ -6620,10 +6617,7 @@ function run() {
if (registryUrl) {
auth.configAuthentication(registryUrl, alwaysAuth);
}
if (cache) {
if (isGhes()) {
throw new Error('Caching is not supported on GHES');
}
if (cache && cache_utils_1.isCacheFeatureAvailable()) {
const cacheDependencyPath = core.getInput('cache-dependency-path');
yield cache_restore_1.restoreCache(cache, cacheDependencyPath);
}
Expand All @@ -6638,10 +6632,6 @@ function run() {
});
}
exports.run = run;
function isGhes() {
const ghUrl = new url_1.URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
function resolveVersionInput() {
let version = core.getInput('node-version');
const versionFileInput = core.getInput('node-version-file');
Expand Down Expand Up @@ -9525,7 +9515,8 @@ function downloadCacheStorageSDK(archiveLocation, archivePath, options) {
//
// If the file exceeds the buffer maximum length (~1 GB on 32-bit systems and ~2 GB
// on 64-bit systems), split the download into multiple segments
const maxSegmentSize = buffer.constants.MAX_LENGTH;
// ~2 GB = 2147483647, beyond this, we start getting out of range error. So, capping it accordingly.
const maxSegmentSize = Math.min(2147483647, buffer.constants.MAX_LENGTH);
const downloadProgress = new DownloadProgress(contentLength);
const fd = fs.openSync(archivePath, 'w');
try {
Expand Down Expand Up @@ -46070,6 +46061,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
const core = __importStar(__webpack_require__(470));
const exec = __importStar(__webpack_require__(986));
const cache = __importStar(__webpack_require__(638));
exports.supportedPackageManagers = {
npm: {
lockFilePatterns: ['package-lock.json', 'yarn.lock'],
Expand Down Expand Up @@ -46134,6 +46126,24 @@ exports.getCacheDirectoryPath = (packageManagerInfo, packageManager) => __awaite
core.debug(`${packageManager} path is ${stdOut}`);
return stdOut;
});
function isGhes() {
const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com');
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
}
exports.isGhes = isGhes;
function isCacheFeatureAvailable() {
if (!cache.isFeatureAvailable()) {
if (isGhes()) {
throw new Error('Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if 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;
}
exports.isCacheFeatureAvailable = isCacheFeatureAvailable;


/***/ }),
Expand Down Expand Up @@ -47526,6 +47536,15 @@ function checkKey(key) {
throw new ValidationError(`Key Validation Error: ${key} cannot contain commas.`);
}
}
/**
* isFeatureAvailable to check the presence of Actions cache service
*
* @returns boolean return true if Actions cache service feature is available, otherwise false
*/
function isFeatureAvailable() {
return !!process.env['ACTIONS_CACHE_URL'];
}
exports.isFeatureAvailable = isFeatureAvailable;
/**
* Restores cache from keys
*
Expand Down