mirror of
https://github.com/github/codeql-action.git
synced 2025-12-27 01:30:10 +08:00
162 lines
7.8 KiB
JavaScript
162 lines
7.8 KiB
JavaScript
"use strict";
|
|
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.internalCacheTwirpClient = void 0;
|
|
const core_1 = require("@actions/core");
|
|
const user_agent_1 = require("./user-agent");
|
|
const errors_1 = require("./errors");
|
|
const config_1 = require("../config");
|
|
const cacheUtils_1 = require("../cacheUtils");
|
|
const auth_1 = require("@actions/http-client/lib/auth");
|
|
const http_client_1 = require("@actions/http-client");
|
|
const cache_twirp_client_1 = require("../../generated/results/api/v1/cache.twirp-client");
|
|
const util_1 = require("./util");
|
|
/**
|
|
* This class is a wrapper around the CacheServiceClientJSON class generated by Twirp.
|
|
*
|
|
* It adds retry logic to the request method, which is not present in the generated client.
|
|
*
|
|
* This class is used to interact with cache service v2.
|
|
*/
|
|
class CacheServiceClient {
|
|
constructor(userAgent, maxAttempts, baseRetryIntervalMilliseconds, retryMultiplier) {
|
|
this.maxAttempts = 5;
|
|
this.baseRetryIntervalMilliseconds = 3000;
|
|
this.retryMultiplier = 1.5;
|
|
const token = (0, cacheUtils_1.getRuntimeToken)();
|
|
this.baseUrl = (0, config_1.getCacheServiceURL)();
|
|
if (maxAttempts) {
|
|
this.maxAttempts = maxAttempts;
|
|
}
|
|
if (baseRetryIntervalMilliseconds) {
|
|
this.baseRetryIntervalMilliseconds = baseRetryIntervalMilliseconds;
|
|
}
|
|
if (retryMultiplier) {
|
|
this.retryMultiplier = retryMultiplier;
|
|
}
|
|
this.httpClient = new http_client_1.HttpClient(userAgent, [
|
|
new auth_1.BearerCredentialHandler(token)
|
|
]);
|
|
}
|
|
// This function satisfies the Rpc interface. It is compatible with the JSON
|
|
// JSON generated client.
|
|
request(service, method, contentType, data) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const url = new URL(`/twirp/${service}/${method}`, this.baseUrl).href;
|
|
(0, core_1.debug)(`[Request] ${method} ${url}`);
|
|
const headers = {
|
|
'Content-Type': contentType
|
|
};
|
|
try {
|
|
const { body } = yield this.retryableRequest(() => __awaiter(this, void 0, void 0, function* () { return this.httpClient.post(url, JSON.stringify(data), headers); }));
|
|
return body;
|
|
}
|
|
catch (error) {
|
|
throw new Error(`Failed to ${method}: ${error.message}`);
|
|
}
|
|
});
|
|
}
|
|
retryableRequest(operation) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
let attempt = 0;
|
|
let errorMessage = '';
|
|
let rawBody = '';
|
|
while (attempt < this.maxAttempts) {
|
|
let isRetryable = false;
|
|
try {
|
|
const response = yield operation();
|
|
const statusCode = response.message.statusCode;
|
|
rawBody = yield response.readBody();
|
|
(0, core_1.debug)(`[Response] - ${response.message.statusCode}`);
|
|
(0, core_1.debug)(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`);
|
|
const body = JSON.parse(rawBody);
|
|
(0, util_1.maskSecretUrls)(body);
|
|
(0, core_1.debug)(`Body: ${JSON.stringify(body, null, 2)}`);
|
|
if (this.isSuccessStatusCode(statusCode)) {
|
|
return { response, body };
|
|
}
|
|
isRetryable = this.isRetryableHttpStatusCode(statusCode);
|
|
errorMessage = `Failed request: (${statusCode}) ${response.message.statusMessage}`;
|
|
if (body.msg) {
|
|
if (errors_1.UsageError.isUsageErrorMessage(body.msg)) {
|
|
throw new errors_1.UsageError();
|
|
}
|
|
errorMessage = `${errorMessage}: ${body.msg}`;
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (error instanceof SyntaxError) {
|
|
(0, core_1.debug)(`Raw Body: ${rawBody}`);
|
|
}
|
|
if (error instanceof errors_1.UsageError) {
|
|
throw error;
|
|
}
|
|
if (errors_1.NetworkError.isNetworkErrorCode(error === null || error === void 0 ? void 0 : error.code)) {
|
|
throw new errors_1.NetworkError(error === null || error === void 0 ? void 0 : error.code);
|
|
}
|
|
isRetryable = true;
|
|
errorMessage = error.message;
|
|
}
|
|
if (!isRetryable) {
|
|
throw new Error(`Received non-retryable error: ${errorMessage}`);
|
|
}
|
|
if (attempt + 1 === this.maxAttempts) {
|
|
throw new Error(`Failed to make request after ${this.maxAttempts} attempts: ${errorMessage}`);
|
|
}
|
|
const retryTimeMilliseconds = this.getExponentialRetryTimeMilliseconds(attempt);
|
|
(0, core_1.info)(`Attempt ${attempt + 1} of ${this.maxAttempts} failed with error: ${errorMessage}. Retrying request in ${retryTimeMilliseconds} ms...`);
|
|
yield this.sleep(retryTimeMilliseconds);
|
|
attempt++;
|
|
}
|
|
throw new Error(`Request failed`);
|
|
});
|
|
}
|
|
isSuccessStatusCode(statusCode) {
|
|
if (!statusCode)
|
|
return false;
|
|
return statusCode >= 200 && statusCode < 300;
|
|
}
|
|
isRetryableHttpStatusCode(statusCode) {
|
|
if (!statusCode)
|
|
return false;
|
|
const retryableStatusCodes = [
|
|
http_client_1.HttpCodes.BadGateway,
|
|
http_client_1.HttpCodes.GatewayTimeout,
|
|
http_client_1.HttpCodes.InternalServerError,
|
|
http_client_1.HttpCodes.ServiceUnavailable,
|
|
http_client_1.HttpCodes.TooManyRequests
|
|
];
|
|
return retryableStatusCodes.includes(statusCode);
|
|
}
|
|
sleep(milliseconds) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
|
});
|
|
}
|
|
getExponentialRetryTimeMilliseconds(attempt) {
|
|
if (attempt < 0) {
|
|
throw new Error('attempt should be a positive integer');
|
|
}
|
|
if (attempt === 0) {
|
|
return this.baseRetryIntervalMilliseconds;
|
|
}
|
|
const minTime = this.baseRetryIntervalMilliseconds * Math.pow(this.retryMultiplier, attempt);
|
|
const maxTime = minTime * this.retryMultiplier;
|
|
// returns a random number between minTime and maxTime (exclusive)
|
|
return Math.trunc(Math.random() * (maxTime - minTime) + minTime);
|
|
}
|
|
}
|
|
function internalCacheTwirpClient(options) {
|
|
const client = new CacheServiceClient((0, user_agent_1.getUserAgentString)(), options === null || options === void 0 ? void 0 : options.maxAttempts, options === null || options === void 0 ? void 0 : options.retryIntervalMs, options === null || options === void 0 ? void 0 : options.retryMultiplier);
|
|
return new cache_twirp_client_1.CacheServiceClientJSON(client);
|
|
}
|
|
exports.internalCacheTwirpClient = internalCacheTwirpClient;
|
|
//# sourceMappingURL=cacheTwirpClient.js.map
|