renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
164 lines • 7.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDependency = getDependency;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const zod_1 = require("zod");
const error_messages_1 = require("../../../constants/error-messages");
const logger_1 = require("../../../logger");
const external_host_error_1 = require("../../../types/errors/external-host-error");
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const package_http_cache_provider_1 = require("../../../util/http/cache/package-http-cache-provider");
const regex_1 = require("../../../util/regex");
const timestamp_1 = require("../../../util/timestamp");
const url_1 = require("../../../util/url");
const SHORT_REPO_REGEX = (0, regex_1.regEx)(/^((?<platform>bitbucket|github|gitlab):)?(?<shortRepo>[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)$/);
const platformMapping = {
bitbucket: 'https://bitbucket.org/',
github: 'https://github.com/',
gitlab: 'https://gitlab.com/',
};
const PackageSource = zod_1.z
.union([
zod_1.z
.string()
.nonempty()
.transform((repository) => {
let sourceUrl = null;
const sourceDirectory = null;
const shortMatch = repository.match(SHORT_REPO_REGEX);
if (shortMatch?.groups) {
const { platform = 'github', shortRepo } = shortMatch.groups;
sourceUrl = platformMapping[platform] + shortRepo;
}
else {
sourceUrl = repository;
}
return { sourceUrl, sourceDirectory };
}),
zod_1.z
.object({
url: zod_1.z.string().nonempty().nullish(),
directory: zod_1.z.string().nonempty().nullish(),
})
.transform(({ url, directory }) => {
const res = { sourceUrl: null, sourceDirectory: null };
if (url) {
res.sourceUrl = url;
}
if (directory) {
res.sourceDirectory = directory;
}
return res;
}),
])
.catch({ sourceUrl: null, sourceDirectory: null });
async function getDependency(http, registryUrl, packageName) {
logger_1.logger.trace(`npm.getDependency(${packageName})`);
const packageUrl = (0, url_1.joinUrlParts)(registryUrl, packageName.replace('/', '%2F'));
try {
const cacheProvider = new package_http_cache_provider_1.PackageHttpCacheProvider({
namespace: 'datasource-npm:cache-provider',
checkAuthorizationHeader: false, // We don't rely on whether user token is provided or not
checkCacheControlHeader: true,
});
const options = { cacheProvider };
// set abortOnError for registry.npmjs.org if no hostRule with explicit abortOnError exists
if (registryUrl === 'https://registry.npmjs.org' &&
hostRules.find({ url: 'https://registry.npmjs.org' })?.abortOnError ===
undefined) {
logger_1.logger.trace({ packageName, registry: 'https://registry.npmjs.org' }, 'setting abortOnError hostRule for well known host');
hostRules.add({
matchHost: 'https://registry.npmjs.org',
abortOnError: true,
});
}
const resp = await http.getJsonUnchecked(packageUrl, options);
const { body: res } = resp;
if (!res.versions || !Object.keys(res.versions).length) {
// Registry returned a 200 OK but with no versions
logger_1.logger.debug(`No versions returned for npm dependency ${packageName}`);
return null;
}
const latestVersion = res.versions[res['dist-tags']?.latest ?? ''];
res.repository ??= latestVersion?.repository;
res.homepage ??= latestVersion?.homepage;
const { sourceUrl, sourceDirectory } = PackageSource.parse(res.repository);
// Simplify response before caching and returning
const dep = {
homepage: res.homepage,
releases: [],
tags: res['dist-tags'],
registryUrl,
};
if (sourceUrl) {
dep.sourceUrl = sourceUrl;
}
if (sourceDirectory) {
dep.sourceDirectory = sourceDirectory;
}
if (latestVersion?.deprecated) {
dep.deprecationMessage = `On registry \`${registryUrl}\`, the "latest" version of dependency \`${packageName}\` has the following deprecation notice:\n\n\`${latestVersion.deprecated}\`\n\nMarking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.`;
}
dep.releases = Object.keys(res.versions).map((version) => {
const release = {
version,
gitRef: res.versions?.[version].gitHead,
dependencies: res.versions?.[version].dependencies,
devDependencies: res.versions?.[version].devDependencies,
};
const releaseTimestamp = (0, timestamp_1.asTimestamp)(res.time?.[version]);
if (releaseTimestamp) {
release.releaseTimestamp = releaseTimestamp;
}
if (res.versions?.[version].deprecated) {
release.isDeprecated = true;
}
const nodeConstraint = res.versions?.[version].engines?.node;
if (is_1.default.nonEmptyString(nodeConstraint)) {
release.constraints = { node: [nodeConstraint] };
}
const source = PackageSource.parse(res.versions?.[version].repository);
if (source.sourceUrl && source.sourceUrl !== dep.sourceUrl) {
release.sourceUrl = source.sourceUrl;
}
if (source.sourceDirectory &&
source.sourceDirectory !== dep.sourceDirectory) {
release.sourceDirectory = source.sourceDirectory;
}
if (dep.deprecationMessage) {
release.isDeprecated = true;
}
return release;
});
const isPublic = resp.headers?.['cache-control']
?.toLocaleLowerCase()
?.split((0, regex_1.regEx)(/\s*,\s*/))
?.includes('public');
if (!isPublic) {
dep.isPrivate = true;
}
logger_1.logger.trace({ dep }, 'dep');
return dep;
}
catch (err) {
const actualError = err instanceof external_host_error_1.ExternalHostError ? err.err : err;
const ignoredStatusCodes = [401, 402, 403, 404];
const ignoredResponseCodes = ['ENOTFOUND'];
if (actualError.message === error_messages_1.HOST_DISABLED ||
ignoredStatusCodes.includes(actualError.statusCode) ||
ignoredResponseCodes.includes(actualError.code)) {
return null;
}
if (err instanceof external_host_error_1.ExternalHostError) {
if (actualError.name === 'ParseError' && actualError.body) {
actualError.body = 'err.body deleted by Renovate';
err.err = actualError;
}
throw err;
}
logger_1.logger.debug({ err }, 'Unknown npm lookup error');
return null;
}
}
//# sourceMappingURL=get.js.map