UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

197 lines • 8.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GithubReleaseAttachmentsDatasource = exports.cacheNamespace = void 0; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); const logger_1 = require("../../../logger"); const decorator_1 = require("../../../util/cache/package/decorator"); const graphql_1 = require("../../../util/github/graphql"); const url_1 = require("../../../util/github/url"); const hash_1 = require("../../../util/hash"); const github_1 = require("../../../util/http/github"); const regex_1 = require("../../../util/regex"); const datasource_1 = require("../datasource"); exports.cacheNamespace = 'datasource-github-releases'; function inferHashAlg(digest) { switch (digest.length) { case 64: return 'sha256'; default: case 96: return 'sha512'; } } class GithubReleaseAttachmentsDatasource extends datasource_1.Datasource { static id = 'github-release-attachments'; defaultRegistryUrls = ['https://github.com']; http; releaseTimestampSupport = true; // Note: not sure releaseTimestampNote = 'The release timestamp is determined from the `releaseTimestamp` field in the results.'; sourceUrlSupport = 'package'; sourceUrlNote = 'The source URL is determined by using the `packageName` and `registryUrl`.'; constructor() { super(GithubReleaseAttachmentsDatasource.id); this.http = new github_1.GithubHttp(GithubReleaseAttachmentsDatasource.id); } async findDigestFile(release, digest) { const smallAssets = release.assets.filter((a) => a.size < 5 * 1024); for (const asset of smallAssets) { const res = await this.http.getText(asset.browser_download_url); for (const line of res.body.split(regex_1.newlineRegex)) { const [lineDigest, lineFilename] = line.split((0, regex_1.regEx)(/\s+/), 2); if (lineDigest === digest) { return { assetName: asset.name, digestedFileName: lineFilename, currentVersion: release.tag_name, currentDigest: lineDigest, }; } } } return null; } async downloadAndDigest(asset, algorithm) { const res = this.http.stream(asset.browser_download_url); const digest = await (0, hash_1.hashStream)(res, algorithm); return digest; } async findAssetWithDigest(release, digest) { const algorithm = inferHashAlg(digest); const assetsBySize = release.assets.sort((a, b) => { if (a.size < b.size) { return -1; } if (a.size > b.size) { return 1; } return 0; }); for (const asset of assetsBySize) { const assetDigest = await this.downloadAndDigest(asset, algorithm); if (assetDigest === digest) { return { assetName: asset.name, currentVersion: release.tag_name, currentDigest: assetDigest, }; } } return null; } /** Identify the asset associated with a known digest. */ async findDigestAsset(release, digest) { const digestFile = await this.findDigestFile(release, digest); if (digestFile) { return digestFile; } const asset = await this.findAssetWithDigest(release, digest); return asset; } /** Given a digest asset, find the equivalent digest in a different release. */ async mapDigestAssetToRelease(digestAsset, release) { const current = digestAsset.currentVersion.replace((0, regex_1.regEx)(/^v/), ''); const next = release.tag_name.replace((0, regex_1.regEx)(/^v/), ''); if (digestAsset.digestedFileName) { const checksumAssetName = digestAsset.assetName.replace(current, next); const checksumAsset = release.assets.find((a) => a.name === checksumAssetName); // If the checksum asset is not found in the new release, fall back to the download method if (checksumAsset) { const releaseFilename = digestAsset.digestedFileName.replace(current, next); const res = await this.http.getText(checksumAsset.browser_download_url); for (const line of res.body.split(regex_1.newlineRegex)) { const [lineDigest, lineFn] = line.split((0, regex_1.regEx)(/\s+/), 2); if (lineFn === releaseFilename) { return lineDigest; } } return null; } } const oldFileName = digestAsset.digestedFileName ?? digestAsset.assetName; const fileName = oldFileName.replace(current, next); const asset = release.assets.find((a) => a.name === fileName); if (!asset) { return null; } const algorithm = inferHashAlg(digestAsset.currentDigest); const newDigest = await this.downloadAndDigest(asset, algorithm); return newDigest; } /** * Attempts to resolve the digest for the specified package. * * The `newValue` supplied here should be a valid tag for the GitHub release. * Requires `currentValue` and `currentDigest`. * * There may be many assets attached to the release. This function will: * - Identify the asset pinned by `currentDigest` in the `currentValue` release * - Download small release assets, parse as checksum manifests (e.g. `SHASUMS.txt`). * - Download individual assets until `currentDigest` is encountered. This is limited to sha256 and sha512. * - Map the hashed asset to `newValue` and return the updated digest as a string */ async getDigest({ packageName: repo, currentValue, currentDigest, registryUrl, }, newValue) { logger_1.logger.debug({ repo, currentValue, currentDigest, registryUrl, newValue }, 'getDigest'); if (!currentDigest) { return null; } if (!currentValue) { return currentDigest; } const apiBaseUrl = (0, url_1.getApiBaseUrl)(registryUrl); const { body: currentRelease } = await this.http.getJsonUnchecked(`${apiBaseUrl}repos/${repo}/releases/tags/${currentValue}`); const digestAsset = await this.findDigestAsset(currentRelease, currentDigest); let newDigest; if (!digestAsset || newValue === currentValue) { newDigest = currentDigest; } else { const { body: newRelease } = await this.http.getJsonUnchecked(`${apiBaseUrl}repos/${repo}/releases/tags/${newValue}`); newDigest = await this.mapDigestAssetToRelease(digestAsset, newRelease); } return newDigest; } /** * This function can be used to fetch releases with a customizable versioning * (e.g. semver) and with releases. * * This function will: * - Fetch all releases * - Sanitize the versions if desired (e.g. strip out leading 'v') * - Return a dependency object containing sourceUrl string and releases array */ async getReleases(config) { const releasesResult = await (0, graphql_1.queryReleases)(config, this.http); const releases = releasesResult.map((item) => { const { version, releaseTimestamp, isStable } = item; const result = { version, gitRef: version, releaseTimestamp, }; if (is_1.default.boolean(isStable)) { result.isStable = isStable; } return result; }); const sourceUrl = (0, url_1.getSourceUrl)(config.packageName, config.registryUrl); return { sourceUrl, releases }; } } exports.GithubReleaseAttachmentsDatasource = GithubReleaseAttachmentsDatasource; tslib_1.__decorate([ (0, decorator_1.cache)({ ttlMinutes: 1440, namespace: `datasource-${GithubReleaseAttachmentsDatasource.id}`, key: (release, digest) => `findDigestFile:${release.html_url}:${digest}`, }) ], GithubReleaseAttachmentsDatasource.prototype, "findDigestFile", null); tslib_1.__decorate([ (0, decorator_1.cache)({ ttlMinutes: 1440, namespace: `datasource-${GithubReleaseAttachmentsDatasource.id}`, key: (asset, algorithm) => `downloadAndDigest:${asset.browser_download_url}:${algorithm}`, }) ], GithubReleaseAttachmentsDatasource.prototype, "downloadAndDigest", null); //# sourceMappingURL=index.js.map