renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
193 lines • 8.45 kB
JavaScript
;
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/), '');
const releaseChecksumAssetName = digestAsset.assetName.replace(current, next);
const releaseAsset = release.assets.find((a) => a.name === releaseChecksumAssetName);
if (!releaseAsset) {
return null;
}
if (digestAsset.digestedFileName) {
const releaseFilename = digestAsset.digestedFileName.replace(current, next);
const res = await this.http.getText(releaseAsset.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;
}
}
}
else {
const algorithm = inferHashAlg(digestAsset.currentDigest);
const newDigest = await this.downloadAndDigest(releaseAsset, algorithm);
return newDigest;
}
return null;
}
/**
* 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