From 19f709fe5842cd705a11d3168d8a7b25afba41d3 Mon Sep 17 00:00:00 2001 From: Sergey Dolin Date: Wed, 5 Jul 2023 10:37:35 +0200 Subject: [PATCH] use highlevel actions cache api --- dist/index.js | 374 +----------------- src/classes/actions-cache-hilevel/download.ts | 9 + src/classes/actions-cache-hilevel/upload.ts | 40 ++ .../download.ts | 0 .../http-client.ts | 0 .../http-responses.ts | 0 .../retry.ts | 0 .../upload.ts | 0 src/classes/state/state-cache-storage.ts | 8 +- 9 files changed, 76 insertions(+), 355 deletions(-) create mode 100644 src/classes/actions-cache-hilevel/download.ts create mode 100644 src/classes/actions-cache-hilevel/upload.ts rename src/classes/{actions-cache => actions-cache-internal}/download.ts (100%) rename src/classes/{actions-cache => actions-cache-internal}/http-client.ts (100%) rename src/classes/{actions-cache => actions-cache-internal}/http-responses.ts (100%) rename src/classes/{actions-cache => actions-cache-internal}/retry.ts (100%) rename src/classes/{actions-cache => actions-cache-internal}/upload.ts (100%) diff --git a/dist/index.js b/dist/index.js index 7bfd115f..83915fab 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,7 +1,7 @@ /******/ (() => { // webpackBootstrap /******/ var __webpack_modules__ = ({ -/***/ 8262: +/***/ 8802: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -29,273 +29,20 @@ var __importStar = (this && this.__importStar) || function (mod) { __setModuleDefault(result, mod); return result; }; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.downloadFileFromActionsCache = void 0; -const http_client_1 = __nccwpck_require__(8661); -const retry_1 = __nccwpck_require__(3910); -const http_responses_1 = __nccwpck_require__(7233); -const downloadUtils_1 = __nccwpck_require__(5500); -const core = __importStar(__nccwpck_require__(2186)); -const getCacheArchiveUrl = (httpClient, cacheKey, cacheVersion) => __awaiter(void 0, void 0, void 0, function* () { - // TODO: should work with delete? - const resource = `cache?keys=${cacheKey}&version=${cacheVersion}`; - const response = yield (0, retry_1.retryTypedResponse)('getCacheEntry', () => __awaiter(void 0, void 0, void 0, function* () { return httpClient.getJson((0, http_client_1.getCacheApiUrl)(resource)); })); - // Cache not found - if (response.statusCode === 204) { - core.debug(`There's no cache with key ${cacheKey} & version=${cacheVersion}`); - // List cache for primary key only if cache miss occurs - return null; - } - if (!(0, http_responses_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode}`); - } - const cacheResult = response.result; - core.debug(`getCacheEntry response is:\n${JSON.stringify(cacheResult)}`); - const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation; - if (!cacheDownloadUrl) { - // Cache archiveLocation not found. This should never happen, and hence bail out. - throw new Error('Cache not found.'); - } - return cacheDownloadUrl; -}); -const downloadFileFromActionsCache = (destFileName, cacheKey, cacheVersion) => __awaiter(void 0, void 0, void 0, function* () { - const httpClient = (0, http_client_1.createActionsCacheClient)(); - const archiveUrl = yield getCacheArchiveUrl(httpClient, cacheKey, cacheVersion); - if (!archiveUrl) { - return undefined; - } - yield (0, downloadUtils_1.downloadCacheHttpClient)(archiveUrl, destFileName); -}); +const cache = __importStar(__nccwpck_require__(7799)); +const path_1 = __importDefault(__nccwpck_require__(1017)); +const downloadFileFromActionsCache = (destFileName, cacheKey, cacheVersion) => cache.restoreCache([path_1.default.dirname(destFileName)], cacheKey); exports.downloadFileFromActionsCache = downloadFileFromActionsCache; /***/ }), -/***/ 8661: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getCacheApiUrl = exports.getGitHubActionsApiUrl = exports.createActionsCacheClient = void 0; -const http_client_1 = __nccwpck_require__(6255); -const auth_1 = __nccwpck_require__(5526); -const core = __importStar(__nccwpck_require__(2186)); -const createAcceptHeader = (type, apiVersion) => `${type};api-version=${apiVersion}`; -const getRequestOptions = () => ({ - headers: { - Accept: createAcceptHeader('application/json', '6.0-preview.1') - } -}); -const createActionsCacheClient = () => { - const token = process.env['ACTIONS_RUNTIME_TOKEN'] || ''; - const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token); - return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions()); -}; -exports.createActionsCacheClient = createActionsCacheClient; -const getGitHubActionsApiUrl = (resource) => { - const baseUrl = process.env['GITHUB_API_URL'] || ''; - if (!baseUrl) { - throw new Error('GitHub API Url not found, unable to restore cache.'); - } - const repo = process.env['GITHUB_REPOSITORY']; - const url = `${baseUrl}/repos/${repo}/actions/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -}; -exports.getGitHubActionsApiUrl = getGitHubActionsApiUrl; -const getCacheApiUrl = (resource) => { - const baseUrl = process.env['ACTIONS_CACHE_URL'] || ''; - if (!baseUrl) { - throw new Error('Cache Service Url not found, unable to restore cache.'); - } - const url = `${baseUrl}_apis/artifactcache/${resource}`; - core.debug(`Resource Url: ${url}`); - return url; -}; -exports.getCacheApiUrl = getCacheApiUrl; - - -/***/ }), - -/***/ 7233: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.isServerErrorStatusCode = exports.isSuccessStatusCode = void 0; -const isSuccessStatusCode = (statusCode) => { - if (!statusCode) { - return false; - } - return statusCode >= 200 && statusCode < 300; -}; -exports.isSuccessStatusCode = isSuccessStatusCode; -function isServerErrorStatusCode(statusCode) { - if (!statusCode) { - return true; - } - return statusCode >= 500; -} -exports.isServerErrorStatusCode = isServerErrorStatusCode; - - -/***/ }), - -/***/ 3910: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.retryTypedResponse = exports.retryHttpClientResponse = void 0; -const http_client_1 = __nccwpck_require__(6255); -const http_responses_1 = __nccwpck_require__(7233); -const core = __importStar(__nccwpck_require__(2186)); -const isRetryableStatusCode = (statusCode) => { - if (!statusCode) { - return false; - } - const retryableStatusCodes = [ - http_client_1.HttpCodes.BadGateway, - http_client_1.HttpCodes.ServiceUnavailable, - http_client_1.HttpCodes.GatewayTimeout - ]; - return retryableStatusCodes.includes(statusCode); -}; -const sleep = (milliseconds) => new Promise(resolve => setTimeout(resolve, milliseconds)); -// The default number of retry attempts. -const DefaultRetryAttempts = 2; -// The default delay in milliseconds between retry attempts. -const DefaultRetryDelay = 5000; -const retry = (name, method, getStatusCode, maxAttempts = DefaultRetryAttempts, delay = DefaultRetryDelay, onError = undefined) => __awaiter(void 0, void 0, void 0, function* () { - let errorMessage = ''; - let attempt = 1; - while (attempt <= maxAttempts) { - let response = undefined; - let statusCode = undefined; - let isRetryable = false; - try { - response = yield method(); - } - catch (error) { - if (onError) { - response = onError(error); - } - isRetryable = true; - errorMessage = error.message; - } - if (response) { - statusCode = getStatusCode(response); - if (!(0, http_responses_1.isServerErrorStatusCode)(statusCode)) { - return response; - } - } - if (statusCode) { - isRetryable = isRetryableStatusCode(statusCode); - errorMessage = `Cache service responded with ${statusCode}`; - } - core.debug(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); - if (!isRetryable) { - core.debug(`${name} - Error is not retryable`); - break; - } - yield sleep(delay); - attempt++; - } - throw Error(`${name} failed: ${errorMessage}`); -}); -const retryHttpClientResponse = (name, method, maxAttempts = DefaultRetryAttempts, delay = DefaultRetryDelay) => __awaiter(void 0, void 0, void 0, function* () { - return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); -}); -exports.retryHttpClientResponse = retryHttpClientResponse; -const retryTypedResponse = (name, method, maxAttempts = DefaultRetryAttempts, delay = DefaultRetryDelay) => retry(name, method, (response) => response.statusCode, maxAttempts, delay, -// If the error object contains the statusCode property, extract it and return -// an TypedResponse so it can be processed by the retry logic. -(error) => { - if (error instanceof http_client_1.HttpClientError) { - return { - statusCode: error.statusCode, - result: null, - headers: {}, - error - }; - } - else { - return undefined; - } -}); -exports.retryTypedResponse = retryTypedResponse; - - -/***/ }), - -/***/ 7042: +/***/ 5970: /***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; @@ -337,50 +84,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) { }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.uploadFileToActionsCache = void 0; -const core = __importStar(__nccwpck_require__(2186)); const fs_1 = __importDefault(__nccwpck_require__(7147)); -const cache_1 = __nccwpck_require__(7799); -const http_responses_1 = __nccwpck_require__(7233); -const retry_1 = __nccwpck_require__(3910); +const core = __importStar(__nccwpck_require__(2186)); +const cache = __importStar(__nccwpck_require__(7799)); const github_1 = __nccwpck_require__(5438); const plugin_retry_1 = __nccwpck_require__(6298); -const http_client_1 = __nccwpck_require__(8661); -const uploadChunk = (httpClient) => __awaiter(void 0, void 0, void 0, function* () { }); -const uploadFile = (httpClient, cacheId, filePath, fileSize) => __awaiter(void 0, void 0, void 0, function* () { - if (fileSize <= 0) - return; - const start = 0; - const end = fileSize - 1; - const contentRange = `bytes ${start}-${end}/*`; - core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${contentRange}`); - const additionalHeaders = { - 'Content-Type': 'application/octet-stream', - 'Content-Range': contentRange - }; - const resourceUrl = (0, http_client_1.getCacheApiUrl)(`caches/${cacheId.toString()}`); - const fd = fs_1.default.openSync(filePath, 'r'); - const openStream = () => fs_1.default - .createReadStream(filePath, { - fd, - start, - end, - autoClose: false - }) - .on('error', error => { - throw new Error(`Cache upload failed because file read failed with ${error.message}`); - }); - try { - const uploadChunkResponse = yield (0, retry_1.retryHttpClientResponse)(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(void 0, void 0, void 0, function* () { - return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders); - })); - if (!(0, http_responses_1.isSuccessStatusCode)(uploadChunkResponse.message.statusCode)) { - throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`); - } - } - finally { - fs_1.default.closeSync(fd); - } -}); const resetCacheWithOctokit = (cacheKey) => __awaiter(void 0, void 0, void 0, function* () { const token = core.getInput('repo-token'); const client = (0, github_1.getOctokit)(token, undefined, plugin_retry_1.retry); @@ -400,58 +108,14 @@ const resetCacheWithOctokit = (cacheKey) => __awaiter(void 0, void 0, void 0, fu } } }); -const reserveCache = (httpClient, fileSize, cacheKey, cacheVersion) => __awaiter(void 0, void 0, void 0, function* () { - var _a, _b, _c, _d; - const reserveCacheRequest = { - key: cacheKey, - version: cacheVersion, - cacheSize: fileSize - }; - const response = yield (0, retry_1.retryTypedResponse)('reserveCache', () => __awaiter(void 0, void 0, void 0, function* () { - return httpClient.postJson((0, http_client_1.getCacheApiUrl)('caches'), reserveCacheRequest); - })); - // handle 400 in the special way - if ((response === null || response === void 0 ? void 0 : response.statusCode) === 400) - throw new Error((_b = (_a = response === null || response === void 0 ? void 0 : response.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : `Cache size of ~${Math.round(fileSize / (1024 * 1024))} MB (${fileSize} B) is over the data cap limit, not saving cache.`); - const cacheId = (_c = response === null || response === void 0 ? void 0 : response.result) === null || _c === void 0 ? void 0 : _c.cacheId; - if (cacheId === undefined) - throw new cache_1.ReserveCacheError(`Unable to reserve cache with key ${cacheKey}, another job may be creating this cache. More details: ${(_d = response === null || response === void 0 ? void 0 : response.error) === null || _d === void 0 ? void 0 : _d.message}`); - return cacheId; -}); -const commitCache = (httpClient, cacheId, filesize) => __awaiter(void 0, void 0, void 0, function* () { - const response = (yield (0, retry_1.retryTypedResponse)('commitCache', () => __awaiter(void 0, void 0, void 0, function* () { - return httpClient.postJson((0, http_client_1.getCacheApiUrl)(`caches/${cacheId.toString()}`), { - size: filesize - }); - }))); - if (!(0, http_responses_1.isSuccessStatusCode)(response.statusCode)) { - throw new Error(`Cache service responded with ${response.statusCode} during commit cache.`); - } -}); const uploadFileToActionsCache = (filePath, cacheKey, cacheVersion) => __awaiter(void 0, void 0, void 0, function* () { - try { - yield resetCacheWithOctokit(cacheKey); - const fileSize = fs_1.default.statSync(filePath).size; - if (fileSize === 0) { - core.info(`the cache ${cacheKey} will be removed`); - return; - } - const httpClient = (0, http_client_1.createActionsCacheClient)(); - const cacheId = yield reserveCache(httpClient, fileSize, cacheKey, cacheVersion); - yield uploadFile(httpClient, cacheId, filePath, fileSize); - yield commitCache(httpClient, cacheId, fileSize); - } - catch (error) { - const typedError = error; - if (typedError.name === cache_1.ValidationError.name) { - throw error; - } - if (typedError.name === cache_1.ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); - return; - } - core.warning(`Failed to save: ${typedError.message}`); + yield resetCacheWithOctokit(cacheKey); + const fileSize = fs_1.default.statSync(filePath).size; + if (fileSize === 0) { + core.info(`the cache ${cacheKey} will be removed`); + return; } + cache.saveCache([filePath], cacheKey); }); exports.uploadFileToActionsCache = uploadFileToActionsCache; @@ -2016,8 +1680,12 @@ const fs_1 = __importDefault(__nccwpck_require__(7147)); const path_1 = __importDefault(__nccwpck_require__(1017)); const os_1 = __importDefault(__nccwpck_require__(2037)); const core = __importStar(__nccwpck_require__(2186)); -const upload_1 = __nccwpck_require__(7042); -const download_1 = __nccwpck_require__(8262); +const download_1 = __nccwpck_require__(8802); +const upload_1 = __nccwpck_require__(5970); +/* +import {uploadFileToActionsCache} from '../actions-cache-internal/upload'; +import {downloadFileFromActionsCache} from '../actions-cache-internal/download'; + */ const CACHE_KEY = '_state'; const CACHE_VERSION = '1'; const STATE_FILE = 'state.txt'; diff --git a/src/classes/actions-cache-hilevel/download.ts b/src/classes/actions-cache-hilevel/download.ts new file mode 100644 index 00000000..8fe32a8a --- /dev/null +++ b/src/classes/actions-cache-hilevel/download.ts @@ -0,0 +1,9 @@ +import * as cache from '@actions/cache'; +import path from 'path'; + +export const downloadFileFromActionsCache = ( + destFileName: string, + cacheKey: string, + cacheVersion: string +): Promise => + cache.restoreCache([path.dirname(destFileName)], cacheKey) as Promise; diff --git a/src/classes/actions-cache-hilevel/upload.ts b/src/classes/actions-cache-hilevel/upload.ts new file mode 100644 index 00000000..890eed0a --- /dev/null +++ b/src/classes/actions-cache-hilevel/upload.ts @@ -0,0 +1,40 @@ +import fs from 'fs'; +import * as core from '@actions/core'; +import * as cache from '@actions/cache'; +import {getOctokit} from '@actions/github'; +import {retry as octokitRetry} from '@octokit/plugin-retry'; + +const resetCacheWithOctokit = async (cacheKey: string): Promise => { + const token = core.getInput('repo-token'); + const client = getOctokit(token, undefined, octokitRetry); + // TODO: better way to get repository? + const repo = process.env['GITHUB_REPOSITORY']; + core.debug(`remove cache "${cacheKey}"`); + try { + // TODO: replace with client.rest. + await client.request( + `DELETE /repos/${repo}/actions/caches?key=${cacheKey}` + ); + } catch (error) { + if (error.status) { + core.debug(`Cache ${cacheKey} does not exist`); + } else { + throw error; + } + } +}; +export const uploadFileToActionsCache = async ( + filePath: string, + cacheKey: string, + cacheVersion: string +) => { + await resetCacheWithOctokit(cacheKey); + const fileSize = fs.statSync(filePath).size; + + if (fileSize === 0) { + core.info(`the cache ${cacheKey} will be removed`); + return; + } + + cache.saveCache([filePath], cacheKey); +}; diff --git a/src/classes/actions-cache/download.ts b/src/classes/actions-cache-internal/download.ts similarity index 100% rename from src/classes/actions-cache/download.ts rename to src/classes/actions-cache-internal/download.ts diff --git a/src/classes/actions-cache/http-client.ts b/src/classes/actions-cache-internal/http-client.ts similarity index 100% rename from src/classes/actions-cache/http-client.ts rename to src/classes/actions-cache-internal/http-client.ts diff --git a/src/classes/actions-cache/http-responses.ts b/src/classes/actions-cache-internal/http-responses.ts similarity index 100% rename from src/classes/actions-cache/http-responses.ts rename to src/classes/actions-cache-internal/http-responses.ts diff --git a/src/classes/actions-cache/retry.ts b/src/classes/actions-cache-internal/retry.ts similarity index 100% rename from src/classes/actions-cache/retry.ts rename to src/classes/actions-cache-internal/retry.ts diff --git a/src/classes/actions-cache/upload.ts b/src/classes/actions-cache-internal/upload.ts similarity index 100% rename from src/classes/actions-cache/upload.ts rename to src/classes/actions-cache-internal/upload.ts diff --git a/src/classes/state/state-cache-storage.ts b/src/classes/state/state-cache-storage.ts index 7877a9df..88fb3fb2 100644 --- a/src/classes/state/state-cache-storage.ts +++ b/src/classes/state/state-cache-storage.ts @@ -3,8 +3,12 @@ import fs from 'fs'; import path from 'path'; import os from 'os'; import * as core from '@actions/core'; -import {uploadFileToActionsCache} from '../actions-cache/upload'; -import {downloadFileFromActionsCache} from '../actions-cache/download'; +import {downloadFileFromActionsCache} from '../actions-cache-hilevel/download'; +import {uploadFileToActionsCache} from '../actions-cache-hilevel/upload'; +/* +import {uploadFileToActionsCache} from '../actions-cache-internal/upload'; +import {downloadFileFromActionsCache} from '../actions-cache-internal/download'; + */ const CACHE_KEY = '_state'; const CACHE_VERSION = '1';