mirror of
https://github.com/oven-sh/setup-bun.git
synced 2025-02-24 03:00:39 +08:00
241 lines
12 KiB
JavaScript
241 lines
12 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());
|
|
});
|
|
};
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
|
result["default"] = mod;
|
|
return result;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const core = __importStar(require("@actions/core"));
|
|
const http_client_1 = require("@actions/http-client");
|
|
const auth_1 = require("@actions/http-client/lib/auth");
|
|
const crypto = __importStar(require("crypto"));
|
|
const fs = __importStar(require("fs"));
|
|
const url_1 = require("url");
|
|
const utils = __importStar(require("./cacheUtils"));
|
|
const downloadUtils_1 = require("./downloadUtils");
|
|
const options_1 = require("../options");
|
|
const requestUtils_1 = require("./requestUtils");
|
|
const versionSalt = '1.0';
|
|
function 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;
|
|
}
|
|
function createAcceptHeader(type, apiVersion) {
|
|
return `${type};api-version=${apiVersion}`;
|
|
}
|
|
function getRequestOptions() {
|
|
const requestOptions = {
|
|
headers: {
|
|
Accept: createAcceptHeader('application/json', '6.0-preview.1')
|
|
}
|
|
};
|
|
return requestOptions;
|
|
}
|
|
function createHttpClient() {
|
|
const token = process.env['ACTIONS_RUNTIME_TOKEN'] || '';
|
|
const bearerCredentialHandler = new auth_1.BearerCredentialHandler(token);
|
|
return new http_client_1.HttpClient('actions/cache', [bearerCredentialHandler], getRequestOptions());
|
|
}
|
|
function getCacheVersion(paths, compressionMethod, enableCrossOsArchive = false) {
|
|
const components = paths;
|
|
// Add compression method to cache version to restore
|
|
// compressed cache as per compression method
|
|
if (compressionMethod) {
|
|
components.push(compressionMethod);
|
|
}
|
|
// Only check for windows platforms if enableCrossOsArchive is false
|
|
if (process.platform === 'win32' && !enableCrossOsArchive) {
|
|
components.push('windows-only');
|
|
}
|
|
// Add salt to cache version to support breaking changes in cache entry
|
|
components.push(versionSalt);
|
|
return crypto
|
|
.createHash('sha256')
|
|
.update(components.join('|'))
|
|
.digest('hex');
|
|
}
|
|
exports.getCacheVersion = getCacheVersion;
|
|
function getCacheEntry(keys, paths, options) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const httpClient = createHttpClient();
|
|
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
|
|
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`;
|
|
const response = yield requestUtils_1.retryTypedResponse('getCacheEntry', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
|
|
// Cache not found
|
|
if (response.statusCode === 204) {
|
|
// List cache for primary key only if cache miss occurs
|
|
if (core.isDebug()) {
|
|
yield printCachesListForDiagnostics(keys[0], httpClient, version);
|
|
}
|
|
return null;
|
|
}
|
|
if (!requestUtils_1.isSuccessStatusCode(response.statusCode)) {
|
|
throw new Error(`Cache service responded with ${response.statusCode}`);
|
|
}
|
|
const cacheResult = response.result;
|
|
const cacheDownloadUrl = cacheResult === null || cacheResult === void 0 ? void 0 : cacheResult.archiveLocation;
|
|
if (!cacheDownloadUrl) {
|
|
// Cache achiveLocation not found. This should never happen, and hence bail out.
|
|
throw new Error('Cache not found.');
|
|
}
|
|
core.setSecret(cacheDownloadUrl);
|
|
core.debug(`Cache Result:`);
|
|
core.debug(JSON.stringify(cacheResult));
|
|
return cacheResult;
|
|
});
|
|
}
|
|
exports.getCacheEntry = getCacheEntry;
|
|
function printCachesListForDiagnostics(key, httpClient, version) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const resource = `caches?key=${encodeURIComponent(key)}`;
|
|
const response = yield requestUtils_1.retryTypedResponse('listCache', () => __awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); }));
|
|
if (response.statusCode === 200) {
|
|
const cacheListResult = response.result;
|
|
const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount;
|
|
if (totalCount && totalCount > 0) {
|
|
core.debug(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`);
|
|
for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) {
|
|
core.debug(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function downloadCache(archiveLocation, archivePath, options) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const archiveUrl = new url_1.URL(archiveLocation);
|
|
const downloadOptions = options_1.getDownloadOptions(options);
|
|
if (downloadOptions.useAzureSdk &&
|
|
archiveUrl.hostname.endsWith('.blob.core.windows.net')) {
|
|
// Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability.
|
|
yield downloadUtils_1.downloadCacheStorageSDK(archiveLocation, archivePath, downloadOptions);
|
|
}
|
|
else {
|
|
// Otherwise, download using the Actions http-client.
|
|
yield downloadUtils_1.downloadCacheHttpClient(archiveLocation, archivePath);
|
|
}
|
|
});
|
|
}
|
|
exports.downloadCache = downloadCache;
|
|
// Reserve Cache
|
|
function reserveCache(key, paths, options) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const httpClient = createHttpClient();
|
|
const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive);
|
|
const reserveCacheRequest = {
|
|
key,
|
|
version,
|
|
cacheSize: options === null || options === void 0 ? void 0 : options.cacheSize
|
|
};
|
|
const response = yield requestUtils_1.retryTypedResponse('reserveCache', () => __awaiter(this, void 0, void 0, function* () {
|
|
return httpClient.postJson(getCacheApiUrl('caches'), reserveCacheRequest);
|
|
}));
|
|
return response;
|
|
});
|
|
}
|
|
exports.reserveCache = reserveCache;
|
|
function getContentRange(start, end) {
|
|
// Format: `bytes start-end/filesize
|
|
// start and end are inclusive
|
|
// filesize can be *
|
|
// For a 200 byte chunk starting at byte 0:
|
|
// Content-Range: bytes 0-199/*
|
|
return `bytes ${start}-${end}/*`;
|
|
}
|
|
function uploadChunk(httpClient, resourceUrl, openStream, start, end) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
core.debug(`Uploading chunk of size ${end -
|
|
start +
|
|
1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`);
|
|
const additionalHeaders = {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Range': getContentRange(start, end)
|
|
};
|
|
const uploadChunkResponse = yield requestUtils_1.retryHttpClientResponse(`uploadChunk (start: ${start}, end: ${end})`, () => __awaiter(this, void 0, void 0, function* () {
|
|
return httpClient.sendStream('PATCH', resourceUrl, openStream(), additionalHeaders);
|
|
}));
|
|
if (!requestUtils_1.isSuccessStatusCode(uploadChunkResponse.message.statusCode)) {
|
|
throw new Error(`Cache service responded with ${uploadChunkResponse.message.statusCode} during upload chunk.`);
|
|
}
|
|
});
|
|
}
|
|
function uploadFile(httpClient, cacheId, archivePath, options) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
// Upload Chunks
|
|
const fileSize = utils.getArchiveFileSizeInBytes(archivePath);
|
|
const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`);
|
|
const fd = fs.openSync(archivePath, 'r');
|
|
const uploadOptions = options_1.getUploadOptions(options);
|
|
const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency);
|
|
const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize);
|
|
const parallelUploads = [...new Array(concurrency).keys()];
|
|
core.debug('Awaiting all uploads');
|
|
let offset = 0;
|
|
try {
|
|
yield Promise.all(parallelUploads.map(() => __awaiter(this, void 0, void 0, function* () {
|
|
while (offset < fileSize) {
|
|
const chunkSize = Math.min(fileSize - offset, maxChunkSize);
|
|
const start = offset;
|
|
const end = offset + chunkSize - 1;
|
|
offset += maxChunkSize;
|
|
yield uploadChunk(httpClient, resourceUrl, () => fs
|
|
.createReadStream(archivePath, {
|
|
fd,
|
|
start,
|
|
end,
|
|
autoClose: false
|
|
})
|
|
.on('error', error => {
|
|
throw new Error(`Cache upload failed because file read failed with ${error.message}`);
|
|
}), start, end);
|
|
}
|
|
})));
|
|
}
|
|
finally {
|
|
fs.closeSync(fd);
|
|
}
|
|
return;
|
|
});
|
|
}
|
|
function commitCache(httpClient, cacheId, filesize) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const commitCacheRequest = { size: filesize };
|
|
return yield requestUtils_1.retryTypedResponse('commitCache', () => __awaiter(this, void 0, void 0, function* () {
|
|
return httpClient.postJson(getCacheApiUrl(`caches/${cacheId.toString()}`), commitCacheRequest);
|
|
}));
|
|
});
|
|
}
|
|
function saveCache(cacheId, archivePath, options) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
const httpClient = createHttpClient();
|
|
core.debug('Upload cache');
|
|
yield uploadFile(httpClient, cacheId, archivePath, options);
|
|
// Commit Cache
|
|
core.debug('Commiting cache');
|
|
const cacheSize = utils.getArchiveFileSizeInBytes(archivePath);
|
|
core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`);
|
|
const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize);
|
|
if (!requestUtils_1.isSuccessStatusCode(commitCacheResponse.statusCode)) {
|
|
throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`);
|
|
}
|
|
core.info('Cache saved successfully');
|
|
});
|
|
}
|
|
exports.saveCache = saveCache;
|
|
//# sourceMappingURL=cacheHttpClient.js.map
|