From 7af0f4d168a0918e55e6b14624f672d800c59e96 Mon Sep 17 00:00:00 2001 From: Dmitry Shibanov Date: Tue, 29 Mar 2022 11:29:58 +0200 Subject: [PATCH] add support for ghes caching --- __tests__/cache-restore.test.ts | 4 +++ __tests__/cache-save.test.ts | 4 +++ __tests__/cache-utils.test.ts | 55 +++++++++++++++++++++------------ dist/cache-save/index.js | 40 +++++++++++++++++++++--- dist/setup/index.js | 50 +++++++++++++++++++++++------- package-lock.json | 22 ++++++------- package.json | 2 +- src/cache-utils.ts | 30 ++++++++++++++++++ src/main.ts | 11 ++----- 9 files changed, 161 insertions(+), 57 deletions(-) diff --git a/__tests__/cache-restore.test.ts b/__tests__/cache-restore.test.ts index 27f6fa27..8157cf84 100644 --- a/__tests__/cache-restore.test.ts +++ b/__tests__/cache-restore.test.ts @@ -50,6 +50,7 @@ describe('cache-restore', () => { let debugSpy: jest.SpyInstance; let setOutputSpy: jest.SpyInstance; let getCommandOutputSpy: jest.SpyInstance; + let isCacheActionAvailable: jest.SpyInstance; let restoreCacheSpy: jest.SpyInstance; let hashFilesSpy: jest.SpyInstance; @@ -102,6 +103,9 @@ describe('cache-restore', () => { // cache-utils getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput'); + + isCacheActionAvailable = jest.spyOn(utils, 'isCacheFeatureAvailable'); + isCacheActionAvailable.mockImplementation(() => true); }); describe('Validate provided package manager', () => { diff --git a/__tests__/cache-save.test.ts b/__tests__/cache-save.test.ts index 82db2195..f8ce3436 100644 --- a/__tests__/cache-save.test.ts +++ b/__tests__/cache-save.test.ts @@ -28,6 +28,7 @@ describe('run', () => { let getStateSpy: jest.SpyInstance; let saveCacheSpy: jest.SpyInstance; let getCommandOutputSpy: jest.SpyInstance; + let isCacheActionAvailable: jest.SpyInstance; let hashFilesSpy: jest.SpyInstance; let existsSpy: jest.SpyInstance; @@ -70,6 +71,9 @@ describe('run', () => { // utils getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput'); + + isCacheActionAvailable = jest.spyOn(utils, 'isCacheFeatureAvailable'); + isCacheActionAvailable.mockImplementation(() => true); }); afterEach(() => { diff --git a/__tests__/cache-utils.test.ts b/__tests__/cache-utils.test.ts index 0ceabeb0..df01605f 100644 --- a/__tests__/cache-utils.test.ts +++ b/__tests__/cache-utils.test.ts @@ -1,37 +1,26 @@ 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; beforeEach(() => { process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data'); debugSpy = jest.spyOn(core, 'debug'); debugSpy.mockImplementation(msg => {}); + info = jest.spyOn(core, 'info'); + + isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable'); + getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput'); }); @@ -51,7 +40,35 @@ describe('cache-utils', () => { }); }); + it('isCacheFeatureAvailable is false', () => { + isFeatureAvailable.mockImplementation(() => false); + process.env['GITHUB_SERVER_URL'] = 'https://www.test.com'; + + expect(isCacheFeatureAvailable()).toBe(false); + expect(info).toHaveBeenCalledWith( + '[warning]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 is false', () => { + isFeatureAvailable.mockImplementation(() => false); + process.env['GITHUB_SERVER_URL'] = ''; + + expect(isCacheFeatureAvailable()).toBe(false); + expect(info).toHaveBeenCalledWith( + '[warning]An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions.' + ); + }); + + it('isCacheFeatureAvailable is true', () => { + isFeatureAvailable.mockImplementation(() => true); + + expect(isCacheFeatureAvailable()).toBe(true); + expect(info).not.toHaveBeenCalled(); + }); + afterEach(() => { + process.env['GITHUB_SERVER_URL'] = ''; jest.resetAllMocks(); jest.clearAllMocks(); }); diff --git a/dist/cache-save/index.js b/dist/cache-save/index.js index e29e4f87..143309cb 100644 --- a/dist/cache-save/index.js +++ b/dist/cache-save/index.js @@ -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.'); } @@ -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'], @@ -3875,6 +3873,28 @@ exports.getCacheDirectoryPath = (packageManagerInfo, packageManager) => __awaite core.debug(`${packageManager} path is ${stdOut}`); return stdOut; }); +function logWarning(message) { + const warningPrefix = '[warning]'; + core.info(`${warningPrefix}${message}`); +} +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()) { + logWarning('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 { + logWarning('An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions.'); + } + return false; + } + return true; +} +exports.isCacheFeatureAvailable = isCacheFeatureAvailable; /***/ }), @@ -5703,7 +5723,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 { @@ -43258,6 +43279,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 * diff --git a/dist/setup/index.js b/dist/setup/index.js index b4b1cfdf..57ef4325 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -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.'); } @@ -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* () { @@ -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); @@ -6621,7 +6618,7 @@ function run() { auth.configAuthentication(registryUrl, alwaysAuth); } if (cache) { - if (isGhes()) { + if (!cache_utils_1.isCacheFeatureAvailable()) { throw new Error('Caching is not supported on GHES'); } const cacheDependencyPath = core.getInput('cache-dependency-path'); @@ -6638,10 +6635,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'); @@ -9525,7 +9518,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 { @@ -46070,6 +46064,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'], @@ -46134,6 +46129,28 @@ exports.getCacheDirectoryPath = (packageManagerInfo, packageManager) => __awaite core.debug(`${packageManager} path is ${stdOut}`); return stdOut; }); +function logWarning(message) { + const warningPrefix = '[warning]'; + core.info(`${warningPrefix}${message}`); +} +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()) { + logWarning('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 { + logWarning('An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions.'); + } + return false; + } + return true; +} +exports.isCacheFeatureAvailable = isCacheFeatureAvailable; /***/ }), @@ -47526,6 +47543,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 * diff --git a/package-lock.json b/package-lock.json index 31514838..2fb8dabb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "@actions/cache": "^1.0.8", + "@actions/cache": "^2.0.0", "@actions/core": "^1.6.0", "@actions/exec": "^1.1.0", "@actions/github": "^1.1.0", @@ -32,17 +32,17 @@ } }, "node_modules/@actions/cache": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.8.tgz", - "integrity": "sha512-GWNNB67w93HGJRQXlsV56YqrdAuDoP3esK/mo5mzU8WoDCVjtQgJGsTdkYUX7brswtT7xnI30bWNo1WLKQ8FZQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-2.0.0.tgz", + "integrity": "sha512-d7n8ul6HjWX6oDrNEPoqn8ZvqyyDhp9Uek6WOxALyxGVsXU+8+ND+viD3UfrXVWfs/GQiqI5Eq4cOozZj0yRFQ==", "dependencies": { "@actions/core": "^1.2.6", "@actions/exec": "^1.0.1", "@actions/glob": "^0.1.0", "@actions/http-client": "^1.0.9", "@actions/io": "^1.0.1", - "@azure/ms-rest-js": "^2.0.7", - "@azure/storage-blob": "^12.1.2", + "@azure/ms-rest-js": "^2.6.0", + "@azure/storage-blob": "^12.8.0", "semver": "^6.1.0", "uuid": "^3.3.3" } @@ -5077,17 +5077,17 @@ }, "dependencies": { "@actions/cache": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.8.tgz", - "integrity": "sha512-GWNNB67w93HGJRQXlsV56YqrdAuDoP3esK/mo5mzU8WoDCVjtQgJGsTdkYUX7brswtT7xnI30bWNo1WLKQ8FZQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-2.0.0.tgz", + "integrity": "sha512-d7n8ul6HjWX6oDrNEPoqn8ZvqyyDhp9Uek6WOxALyxGVsXU+8+ND+viD3UfrXVWfs/GQiqI5Eq4cOozZj0yRFQ==", "requires": { "@actions/core": "^1.2.6", "@actions/exec": "^1.0.1", "@actions/glob": "^0.1.0", "@actions/http-client": "^1.0.9", "@actions/io": "^1.0.1", - "@azure/ms-rest-js": "^2.0.7", - "@azure/storage-blob": "^12.1.2", + "@azure/ms-rest-js": "^2.6.0", + "@azure/storage-blob": "^12.8.0", "semver": "^6.1.0", "uuid": "^3.3.3" }, diff --git a/package.json b/package.json index 8c6eee0c..83182472 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "author": "GitHub", "license": "MIT", "dependencies": { - "@actions/cache": "^1.0.8", + "@actions/cache": "^2.0.0", "@actions/core": "^1.6.0", "@actions/exec": "^1.1.0", "@actions/github": "^1.1.0", diff --git a/src/cache-utils.ts b/src/cache-utils.ts index 36531bd5..6b5e8e2a 100644 --- a/src/cache-utils.ts +++ b/src/cache-utils.ts @@ -1,5 +1,6 @@ import * as core from '@actions/core'; import * as exec from '@actions/exec'; +import * as cache from '@actions/cache'; type SupportedPackageManagers = { [prop: string]: PackageManagerInfo; @@ -95,3 +96,32 @@ export const getCacheDirectoryPath = async ( return stdOut; }; + +function logWarning(message: string): void { + const warningPrefix = '[warning]'; + core.info(`${warningPrefix}${message}`); +} + +export function isGhes(): boolean { + const ghUrl = new URL( + process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ); + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; +} + +export function isCacheFeatureAvailable(): boolean { + if (!cache.isFeatureAvailable()) { + if (isGhes()) { + logWarning( + '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 { + logWarning( + 'An internal error has occurred in cache backend. Please check https://www.githubstatus.com/ for any ongoing issue in actions.' + ); + } + return false; + } + + return true; +} diff --git a/src/main.ts b/src/main.ts index 2de86ca8..020f8c25 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import fs from 'fs'; import * as auth from './authutil'; import * as path from 'path'; import {restoreCache} from './cache-restore'; -import {URL} from 'url'; +import {isGhes, isCacheFeatureAvailable} from './cache-utils'; import os = require('os'); export async function run() { @@ -46,7 +46,7 @@ export async function run() { } if (cache) { - if (isGhes()) { + if (!isCacheFeatureAvailable()) { throw new Error('Caching is not supported on GHES'); } const cacheDependencyPath = core.getInput('cache-dependency-path'); @@ -66,13 +66,6 @@ export async function run() { } } -function isGhes(): boolean { - const ghUrl = new URL( - process.env['GITHUB_SERVER_URL'] || 'https://github.com' - ); - return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'; -} - function resolveVersionInput(): string { let version = core.getInput('node-version'); const versionFileInput = core.getInput('node-version-file');