Files
codeql-action/node_modules/@actions/artifact/lib/internal/download-http-client.js
dependabot[bot] dd1128f4f1 Bump the npm group with 11 updates (#1892)
* Bump the npm group with 11 updates

Bumps the npm group with 11 updates:

| Package | From | To |
| --- | --- | --- |
| [@actions/artifact](https://github.com/actions/toolkit/tree/HEAD/packages/artifact) | `1.1.1` | `1.1.2` |
| [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) | `1.10.0` | `1.10.1` |
| [uuid](https://github.com/uuidjs/uuid) | `9.0.0` | `9.0.1` |
| [@types/uuid](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/uuid) | `9.0.3` | `9.0.4` |
| [@types/adm-zip](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/adm-zip) | `0.5.0` | `0.5.1` |
| [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) | `4.0.5` | `4.0.6` |
| [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver) | `7.5.1` | `7.5.2` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `6.5.0` | `6.7.2` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `6.6.0` | `6.7.2` |
| [eslint](https://github.com/eslint/eslint) | `8.48.0` | `8.49.0` |
| [sinon](https://github.com/sinonjs/sinon) | `15.2.0` | `16.0.0` |


Updates `@actions/artifact` from 1.1.1 to 1.1.2
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/artifact/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/artifact)

Updates `@actions/core` from 1.10.0 to 1.10.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `uuid` from 9.0.0 to 9.0.1
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v9.0.0...v9.0.1)

Updates `@types/uuid` from 9.0.3 to 9.0.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/uuid)

Updates `@types/adm-zip` from 0.5.0 to 0.5.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/adm-zip)

Updates `@types/js-yaml` from 4.0.5 to 4.0.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml)

Updates `@types/semver` from 7.5.1 to 7.5.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver)

Updates `@typescript-eslint/eslint-plugin` from 6.5.0 to 6.7.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.7.2/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 6.6.0 to 6.7.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.7.2/packages/parser)

Updates `eslint` from 8.48.0 to 8.49.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.48.0...v8.49.0)

Updates `sinon` from 15.2.0 to 16.0.0
- [Release notes](https://github.com/sinonjs/sinon/releases)
- [Changelog](https://github.com/sinonjs/sinon/blob/main/docs/changelog.md)
- [Commits](https://github.com/sinonjs/sinon/compare/v15.2.0...v16.0.0)

---
updated-dependencies:
- dependency-name: "@actions/artifact"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@actions/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: uuid
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/uuid"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/adm-zip"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/js-yaml"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@types/semver"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: sinon
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update checked-in dependencies

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-09-18 20:01:16 +00:00

296 lines
16 KiB
JavaScript

"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.DownloadHttpClient = void 0;
const fs = __importStar(require("fs"));
const core = __importStar(require("@actions/core"));
const zlib = __importStar(require("zlib"));
const utils_1 = require("./utils");
const url_1 = require("url");
const status_reporter_1 = require("./status-reporter");
const perf_hooks_1 = require("perf_hooks");
const http_manager_1 = require("./http-manager");
const config_variables_1 = require("./config-variables");
const requestUtils_1 = require("./requestUtils");
class DownloadHttpClient {
constructor() {
this.downloadHttpManager = new http_manager_1.HttpManager((0, config_variables_1.getDownloadFileConcurrency)(), '@actions/artifact-download');
// downloads are usually significantly faster than uploads so display status information every second
this.statusReporter = new status_reporter_1.StatusReporter(1000);
}
/**
* Gets a list of all artifacts that are in a specific container
*/
listArtifacts() {
return __awaiter(this, void 0, void 0, function* () {
const artifactUrl = (0, utils_1.getArtifactUrl)();
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
const client = this.downloadHttpManager.getClient(0);
const headers = (0, utils_1.getDownloadHeaders)('application/json');
const response = yield (0, requestUtils_1.retryHttpClientRequest)('List Artifacts', () => __awaiter(this, void 0, void 0, function* () { return client.get(artifactUrl, headers); }));
const body = yield response.readBody();
return JSON.parse(body);
});
}
/**
* Fetches a set of container items that describe the contents of an artifact
* @param artifactName the name of the artifact
* @param containerUrl the artifact container URL for the run
*/
getContainerItems(artifactName, containerUrl) {
return __awaiter(this, void 0, void 0, function* () {
// the itemPath search parameter controls which containers will be returned
const resourceUrl = new url_1.URL(containerUrl);
resourceUrl.searchParams.append('itemPath', artifactName);
// use the first client from the httpManager, `keep-alive` is not used so the connection will close immediately
const client = this.downloadHttpManager.getClient(0);
const headers = (0, utils_1.getDownloadHeaders)('application/json');
const response = yield (0, requestUtils_1.retryHttpClientRequest)('Get Container Items', () => __awaiter(this, void 0, void 0, function* () { return client.get(resourceUrl.toString(), headers); }));
const body = yield response.readBody();
return JSON.parse(body);
});
}
/**
* Concurrently downloads all the files that are part of an artifact
* @param downloadItems information about what items to download and where to save them
*/
downloadSingleArtifact(downloadItems) {
return __awaiter(this, void 0, void 0, function* () {
const DOWNLOAD_CONCURRENCY = (0, config_variables_1.getDownloadFileConcurrency)();
// limit the number of files downloaded at a single time
core.debug(`Download file concurrency is set to ${DOWNLOAD_CONCURRENCY}`);
const parallelDownloads = [...new Array(DOWNLOAD_CONCURRENCY).keys()];
let currentFile = 0;
let downloadedFiles = 0;
core.info(`Total number of files that will be downloaded: ${downloadItems.length}`);
this.statusReporter.setTotalNumberOfFilesToProcess(downloadItems.length);
this.statusReporter.start();
yield Promise.all(parallelDownloads.map((index) => __awaiter(this, void 0, void 0, function* () {
while (currentFile < downloadItems.length) {
const currentFileToDownload = downloadItems[currentFile];
currentFile += 1;
const startTime = perf_hooks_1.performance.now();
yield this.downloadIndividualFile(index, currentFileToDownload.sourceLocation, currentFileToDownload.targetPath);
if (core.isDebug()) {
core.debug(`File: ${++downloadedFiles}/${downloadItems.length}. ${currentFileToDownload.targetPath} took ${(perf_hooks_1.performance.now() - startTime).toFixed(3)} milliseconds to finish downloading`);
}
this.statusReporter.incrementProcessedCount();
}
})))
.catch(error => {
throw new Error(`Unable to download the artifact: ${error}`);
})
.finally(() => {
this.statusReporter.stop();
// safety dispose all connections
this.downloadHttpManager.disposeAndReplaceAllClients();
});
});
}
/**
* Downloads an individual file
* @param httpClientIndex the index of the http client that is used to make all of the calls
* @param artifactLocation origin location where a file will be downloaded from
* @param downloadPath destination location for the file being downloaded
*/
downloadIndividualFile(httpClientIndex, artifactLocation, downloadPath) {
return __awaiter(this, void 0, void 0, function* () {
let retryCount = 0;
const retryLimit = (0, config_variables_1.getRetryLimit)();
let destinationStream = fs.createWriteStream(downloadPath);
const headers = (0, utils_1.getDownloadHeaders)('application/json', true, true);
// a single GET request is used to download a file
const makeDownloadRequest = () => __awaiter(this, void 0, void 0, function* () {
const client = this.downloadHttpManager.getClient(httpClientIndex);
return yield client.get(artifactLocation, headers);
});
// check the response headers to determine if the file was compressed using gzip
const isGzip = (incomingHeaders) => {
return ('content-encoding' in incomingHeaders &&
incomingHeaders['content-encoding'] === 'gzip');
};
// Increments the current retry count and then checks if the retry limit has been reached
// If there have been too many retries, fail so the download stops. If there is a retryAfterValue value provided,
// it will be used
const backOff = (retryAfterValue) => __awaiter(this, void 0, void 0, function* () {
retryCount++;
if (retryCount > retryLimit) {
return Promise.reject(new Error(`Retry limit has been reached. Unable to download ${artifactLocation}`));
}
else {
this.downloadHttpManager.disposeAndReplaceClient(httpClientIndex);
if (retryAfterValue) {
// Back off by waiting the specified time denoted by the retry-after header
core.info(`Backoff due to too many requests, retry #${retryCount}. Waiting for ${retryAfterValue} milliseconds before continuing the download`);
yield (0, utils_1.sleep)(retryAfterValue);
}
else {
// Back off using an exponential value that depends on the retry count
const backoffTime = (0, utils_1.getExponentialRetryTimeInMilliseconds)(retryCount);
core.info(`Exponential backoff for retry #${retryCount}. Waiting for ${backoffTime} milliseconds before continuing the download`);
yield (0, utils_1.sleep)(backoffTime);
}
core.info(`Finished backoff for retry #${retryCount}, continuing with download`);
}
});
const isAllBytesReceived = (expected, received) => {
// be lenient, if any input is missing, assume success, i.e. not truncated
if (!expected ||
!received ||
process.env['ACTIONS_ARTIFACT_SKIP_DOWNLOAD_VALIDATION']) {
core.info('Skipping download validation.');
return true;
}
return parseInt(expected) === received;
};
const resetDestinationStream = (fileDownloadPath) => __awaiter(this, void 0, void 0, function* () {
destinationStream.close();
// await until file is created at downloadpath; node15 and up fs.createWriteStream had not created a file yet
yield new Promise(resolve => {
destinationStream.on('close', resolve);
if (destinationStream.writableFinished) {
resolve();
}
});
yield (0, utils_1.rmFile)(fileDownloadPath);
destinationStream = fs.createWriteStream(fileDownloadPath);
});
// keep trying to download a file until a retry limit has been reached
while (retryCount <= retryLimit) {
let response;
try {
response = yield makeDownloadRequest();
}
catch (error) {
// if an error is caught, it is usually indicative of a timeout so retry the download
core.info('An error occurred while attempting to download a file');
// eslint-disable-next-line no-console
console.log(error);
// increment the retryCount and use exponential backoff to wait before making the next request
yield backOff();
continue;
}
let forceRetry = false;
if ((0, utils_1.isSuccessStatusCode)(response.message.statusCode)) {
// The body contains the contents of the file however calling response.readBody() causes all the content to be converted to a string
// which can cause some gzip encoded data to be lost
// Instead of using response.readBody(), response.message is a readableStream that can be directly used to get the raw body contents
try {
const isGzipped = isGzip(response.message.headers);
yield this.pipeResponseToFile(response, destinationStream, isGzipped);
if (isGzipped ||
isAllBytesReceived(response.message.headers['content-length'], yield (0, utils_1.getFileSize)(downloadPath))) {
return;
}
else {
forceRetry = true;
}
}
catch (error) {
// retry on error, most likely streams were corrupted
forceRetry = true;
}
}
if (forceRetry || (0, utils_1.isRetryableStatusCode)(response.message.statusCode)) {
core.info(`A ${response.message.statusCode} response code has been received while attempting to download an artifact`);
resetDestinationStream(downloadPath);
// if a throttled status code is received, try to get the retryAfter header value, else differ to standard exponential backoff
(0, utils_1.isThrottledStatusCode)(response.message.statusCode)
? yield backOff((0, utils_1.tryGetRetryAfterValueTimeInMilliseconds)(response.message.headers))
: yield backOff();
}
else {
// Some unexpected response code, fail immediately and stop the download
(0, utils_1.displayHttpDiagnostics)(response);
return Promise.reject(new Error(`Unexpected http ${response.message.statusCode} during download for ${artifactLocation}`));
}
}
});
}
/**
* Pipes the response from downloading an individual file to the appropriate destination stream while decoding gzip content if necessary
* @param response the http response received when downloading a file
* @param destinationStream the stream where the file should be written to
* @param isGzip a boolean denoting if the content is compressed using gzip and if we need to decode it
*/
pipeResponseToFile(response, destinationStream, isGzip) {
return __awaiter(this, void 0, void 0, function* () {
yield new Promise((resolve, reject) => {
if (isGzip) {
const gunzip = zlib.createGunzip();
response.message
.on('error', error => {
core.info(`An error occurred while attempting to read the response stream`);
gunzip.close();
destinationStream.close();
reject(error);
})
.pipe(gunzip)
.on('error', error => {
core.info(`An error occurred while attempting to decompress the response stream`);
destinationStream.close();
reject(error);
})
.pipe(destinationStream)
.on('close', () => {
resolve();
})
.on('error', error => {
core.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`);
reject(error);
});
}
else {
response.message
.on('error', error => {
core.info(`An error occurred while attempting to read the response stream`);
destinationStream.close();
reject(error);
})
.pipe(destinationStream)
.on('close', () => {
resolve();
})
.on('error', error => {
core.info(`An error occurred while writing a downloaded file to ${destinationStream.path}`);
reject(error);
});
}
});
return;
});
}
}
exports.DownloadHttpClient = DownloadHttpClient;
//# sourceMappingURL=download-http-client.js.map