renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
134 lines (133 loc) • 5.95 kB
JavaScript
import { logger } from "../../../logger/index.js";
import { replaceUrlPath, resolveBaseUrl } from "../../../util/url.js";
import { find } from "../../../util/host-rules.js";
import { ExternalHostError } from "../../../types/errors/external-host-error.js";
import { id } from "../../versioning/composer/index.js";
import { withCache } from "../../../util/cache/package/with-cache.js";
import { Datasource } from "../datasource.js";
import { map } from "../../../util/promises.js";
import { PackagesResponse, PackagistFile, RegistryMeta, extractDepReleases, parsePackagesResponses } from "./schema.js";
import { isObject } from "@sindresorhus/is";
import { z } from "zod/v4";
//#region lib/modules/datasource/packagist/index.ts
var PackagistDatasource = class PackagistDatasource extends Datasource {
static id = "packagist";
constructor() {
super(PackagistDatasource.id);
}
defaultRegistryUrls = ["https://repo.packagist.org"];
defaultVersioning = id;
registryStrategy = "hunt";
releaseTimestampSupport = true;
releaseTimestampNote = "The release timestamp is determined from the `time` field in the results.";
sourceUrlSupport = "package";
sourceUrlNote = "The source URL is determined from `source` field in the results.";
static getHostOpts(url) {
const { username, password } = find({
hostType: PackagistDatasource.id,
url
});
return username && password ? {
username,
password
} : {};
}
async getJson(url, schema) {
const opts = PackagistDatasource.getHostOpts(url);
const { body } = await this.http.getJson(url, opts, schema);
return body;
}
async _getRegistryMeta(regUrl) {
const url = resolveBaseUrl(regUrl, "packages.json");
return await this.getJson(url, RegistryMeta);
}
getRegistryMeta(regUrl) {
return withCache({
namespace: `datasource-${PackagistDatasource.id}`,
key: `getRegistryMeta:${regUrl}`
}, () => this._getRegistryMeta(regUrl));
}
static isPrivatePackage(regUrl) {
return !!PackagistDatasource.getHostOpts(regUrl).password;
}
static getPackagistFileUrl(regUrl, regFile) {
const { key, hash } = regFile;
return resolveBaseUrl(regUrl, hash ? key.replace("%hash%", hash) : /* istanbul ignore next: hard to test */ key);
}
async _getPackagistFile(regUrl, regFile) {
const url = PackagistDatasource.getPackagistFileUrl(regUrl, regFile);
return await this.getJson(url, PackagistFile);
}
getPackagistFile(regUrl, regFile) {
return withCache({
namespace: `datasource-${PackagistDatasource.id}`,
key: `getPackagistFile:${PackagistDatasource.getPackagistFileUrl(regUrl, regFile)}`,
ttlMinutes: 1440,
cacheable: !PackagistDatasource.isPrivatePackage(regUrl)
}, () => this._getPackagistFile(regUrl, regFile));
}
async fetchProviderPackages(regUrl, meta) {
await map(meta.files, async (file) => {
const res = await this.getPackagistFile(regUrl, file);
Object.assign(meta.providerPackages, res.providers);
});
}
async fetchIncludesPackages(regUrl, meta) {
await map(meta.includesFiles, async (file) => {
const res = await this.getPackagistFile(regUrl, file);
for (const [key, val] of Object.entries(res.packages)) meta.includesPackages[key] = extractDepReleases(val);
});
}
async _packagistV2Lookup(registryUrl, metadataUrl, packageName) {
const pkgUrl = replaceUrlPath(registryUrl, metadataUrl.replace("%package%", packageName));
const pkgPromise = this.getJson(pkgUrl, z.unknown());
const devUrl = replaceUrlPath(registryUrl, metadataUrl.replace("%package%", `${packageName}~dev`));
const devPromise = this.getJson(devUrl, z.unknown()).then((x) => x, () => null);
return parsePackagesResponses(packageName, await Promise.all([pkgPromise, devPromise]).then((responses) => responses.filter(isObject)));
}
packagistV2Lookup(registryUrl, metadataUrl, packageName) {
return withCache({
namespace: `datasource-${PackagistDatasource.id}`,
key: `packagistV2Lookup:${registryUrl}:${metadataUrl}:${packageName}`,
ttlMinutes: 10
}, () => this._packagistV2Lookup(registryUrl, metadataUrl, packageName));
}
getPkgUrl(packageName, registryUrl, registryMeta) {
if (registryMeta.providersUrl && packageName in registryMeta.providerPackages) {
let url = registryMeta.providersUrl.replace("%package%", packageName);
const hash = registryMeta.providerPackages[packageName];
if (hash) url = url.replace("%hash%", hash);
return replaceUrlPath(registryUrl, url);
}
if (registryMeta.providersLazyUrl) return replaceUrlPath(registryUrl, registryMeta.providersLazyUrl.replace("%package%", packageName));
return null;
}
async getReleases({ packageName, registryUrl }) {
logger.trace(`getReleases(${packageName})`);
/* v8 ignore next 3 -- should never happen */
if (!registryUrl) return null;
try {
const meta = await this.getRegistryMeta(registryUrl);
if (meta.availablePackages && !meta.availablePackages.includes(packageName)) return null;
if (meta.metadataUrl) return await this.packagistV2Lookup(registryUrl, meta.metadataUrl, packageName);
if (meta.packages[packageName]) return extractDepReleases(meta.packages[packageName]);
await this.fetchIncludesPackages(registryUrl, meta);
if (meta.includesPackages[packageName]) return meta.includesPackages[packageName];
await this.fetchProviderPackages(registryUrl, meta);
const pkgUrl = this.getPkgUrl(packageName, registryUrl, meta);
if (!pkgUrl) return null;
const dep = extractDepReleases((await this.getJson(pkgUrl, PackagesResponse)).packages[packageName]);
logger.trace({ dep }, "dep");
return dep;
} catch (err) /* istanbul ignore next */ {
if (err.host === "packagist.org") {
if (err.code === "ECONNRESET" || err.code === "ETIMEDOUT") throw new ExternalHostError(err);
if (err.statusCode && err.statusCode >= 500 && err.statusCode < 600) throw new ExternalHostError(err);
}
throw err;
}
}
};
//#endregion
export { PackagistDatasource };
//# sourceMappingURL=index.js.map