mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-02 02:29:51 +00:00
3ed45d1efd
* fix(datasource): replace getJsonUnchecked with validated getJson in misc+merge-confidence group Replaces all getJsonUnchecked calls with Zod-validated getJson calls in: - crate: adds CrateMetadataSchema to existing schema.ts - conan: uses existing ConanJSON schema directly - puppet-forge: new schema.ts with PuppetModuleSchema - conda: new schema.ts with CondaPackageSchema - repology: new schema.ts with RepologyPackagesSchema - jenkins-plugins: new schema.ts, parameterizes helper with ZodType<T> - custom/formats/json: uses z.unknown() for user-defined responses - merge-confidence: adds inline MergeConfidenceResponseSchema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: correct nullable/optional schema fields for crate, jenkins-plugins, puppet-forge - crate: remove .optional() from all CrateMetadata fields to match string|null type - jenkins-plugins: use .default({}) instead of .optional() for plugins fields so schemas match required Record types; widen ZodType input to unknown; update JenkinsPluginVersion.releaseTimestamp to Timestamp|null to match MaybeTimestamp output - puppet-forge: change releases from .optional() to .default([]) to match Release[] type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: migrate zod/v3 imports to zod/v4 in misc datasources and merge-confidence Update remaining zod/v3 imports in files added/modified by the getJsonUnchecked migration to use zod/v4 as required after the upstream migration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore minor improvements * remove unnecessary types and reintroduce nullable where necessary * refactor: move logic into transform * test: add tests to improve coverage * chore removed some unused types * remove type declaration --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
215 lines
6.7 KiB
TypeScript
215 lines
6.7 KiB
TypeScript
import { isString, isUndefined } from '@sindresorhus/is';
|
|
import { logger } from '../../../logger/index.ts';
|
|
import { withCache } from '../../../util/cache/package/with-cache.ts';
|
|
import { GithubHttp } from '../../../util/http/github.ts';
|
|
import { regEx } from '../../../util/regex.ts';
|
|
import { ensureTrailingSlash, joinUrlParts } from '../../../util/url.ts';
|
|
import * as allVersioning from '../../versioning/index.ts';
|
|
import { Datasource } from '../datasource.ts';
|
|
import type {
|
|
DigestConfig,
|
|
GetReleasesConfig,
|
|
Release,
|
|
ReleaseResult,
|
|
} from '../types.ts';
|
|
import { isArtifactoryServer } from '../util.ts';
|
|
import { datasource, defaultRegistryUrl, getConanPackage } from './common.ts';
|
|
import {
|
|
ConanCenterReleases,
|
|
ConanJSON,
|
|
ConanLatestRevision,
|
|
ConanProperties,
|
|
ConanRevisionJSON,
|
|
} from './schema.ts';
|
|
|
|
export class ConanDatasource extends Datasource {
|
|
static readonly id = datasource;
|
|
|
|
override readonly defaultRegistryUrls = [defaultRegistryUrl];
|
|
|
|
override readonly caching = true;
|
|
|
|
override readonly registryStrategy = 'merge';
|
|
|
|
githubHttp: GithubHttp;
|
|
|
|
override readonly sourceUrlSupport = 'package';
|
|
override readonly sourceUrlNote =
|
|
'The source URL is supported only if the package is served from the Artifactory servers. In which case we determine it from the `properties[conan.package.url]` field in the results.';
|
|
|
|
constructor(id = ConanDatasource.id) {
|
|
super(id);
|
|
this.githubHttp = new GithubHttp(id);
|
|
}
|
|
|
|
async getConanCenterReleases(
|
|
conanName: string,
|
|
userAndChannel: string,
|
|
): Promise<ReleaseResult | null> {
|
|
if (userAndChannel && userAndChannel !== '@_/_') {
|
|
logger.debug(
|
|
{ conanName, userAndChannel },
|
|
'User/channel not supported for Conan Center lookups',
|
|
);
|
|
return null;
|
|
}
|
|
const url = `https://api.github.com/repos/conan-io/conan-center-index/contents/recipes/${conanName}/config.yml`;
|
|
const { body: result } = await this.githubHttp.getYaml(
|
|
url,
|
|
{ headers: { accept: 'application/vnd.github.v3.raw' } },
|
|
ConanCenterReleases,
|
|
);
|
|
return result;
|
|
}
|
|
|
|
private async _getDigest(
|
|
{ registryUrl, packageName }: DigestConfig,
|
|
newValue?: string,
|
|
): Promise<string | null> {
|
|
if (isUndefined(newValue) || isUndefined(registryUrl)) {
|
|
return null;
|
|
}
|
|
const url = ensureTrailingSlash(registryUrl);
|
|
const conanPackage = getConanPackage(packageName);
|
|
const revisionLookUp = joinUrlParts(
|
|
url,
|
|
'v2/conans/',
|
|
conanPackage.conanName,
|
|
newValue,
|
|
conanPackage.userAndChannel,
|
|
'/revisions',
|
|
);
|
|
const { body: digest } = await this.http.getJson(
|
|
revisionLookUp,
|
|
ConanLatestRevision,
|
|
);
|
|
return digest;
|
|
}
|
|
|
|
override getDigest(
|
|
config: DigestConfig,
|
|
newValue?: string,
|
|
): Promise<string | null> {
|
|
return withCache(
|
|
{
|
|
namespace: `datasource-${datasource}`,
|
|
// TODO: types (#22198)
|
|
key: `getDigest:${config.registryUrl!}:${config.packageName}:${newValue!}`,
|
|
fallback: true,
|
|
},
|
|
() => this._getDigest(config, newValue),
|
|
);
|
|
}
|
|
|
|
private async _getReleases({
|
|
registryUrl,
|
|
packageName,
|
|
}: GetReleasesConfig): Promise<ReleaseResult | null> {
|
|
const conanPackage = getConanPackage(packageName);
|
|
const userAndChannel = `@${conanPackage.userAndChannel}`;
|
|
if (
|
|
isString(registryUrl) &&
|
|
ensureTrailingSlash(registryUrl) === defaultRegistryUrl
|
|
) {
|
|
return this.getConanCenterReleases(
|
|
conanPackage.conanName,
|
|
userAndChannel,
|
|
);
|
|
}
|
|
|
|
logger.trace(
|
|
{ packageName, registryUrl },
|
|
'Looking up conan api dependency',
|
|
);
|
|
|
|
if (registryUrl) {
|
|
const url = ensureTrailingSlash(registryUrl);
|
|
const lookupUrl = joinUrlParts(
|
|
url,
|
|
`v2/conans/search?q=${conanPackage.conanName}`,
|
|
);
|
|
|
|
try {
|
|
const rep = await this.http.getJson(lookupUrl, ConanJSON);
|
|
const conanJson = rep.body;
|
|
if (conanJson) {
|
|
logger.trace({ lookupUrl }, 'Got conan api result');
|
|
const dep: ReleaseResult = { releases: [] };
|
|
|
|
const conanJsonReleases: Release[] = conanJson
|
|
.filter(({ userChannel }) => userChannel === userAndChannel)
|
|
.map(({ version }) => ({ version }));
|
|
dep.releases.push(...conanJsonReleases);
|
|
|
|
try {
|
|
if (isArtifactoryServer(rep)) {
|
|
const conanApiRegexp = regEx(
|
|
/(?<host>.*)\/artifactory\/api\/conan\/(?<repo>[^/]+)/,
|
|
);
|
|
const groups = conanApiRegexp.exec(url)?.groups;
|
|
if (!groups) {
|
|
return dep;
|
|
}
|
|
const semver = allVersioning.get('semver');
|
|
|
|
const sortedReleases = dep.releases
|
|
.filter((release) => semver.isVersion(release.version))
|
|
.sort((a, b) => semver.sortVersions(a.version, b.version));
|
|
|
|
const latestVersion = sortedReleases.at(-1)?.version;
|
|
|
|
if (!latestVersion) {
|
|
return dep;
|
|
}
|
|
logger.debug(
|
|
`Conan package ${packageName} has latest version ${latestVersion}`,
|
|
);
|
|
|
|
const latestRevisionUrl = joinUrlParts(
|
|
url,
|
|
`v2/conans/${conanPackage.conanName}/${latestVersion}/${conanPackage.userAndChannel}/latest`,
|
|
);
|
|
const {
|
|
body: { revision: packageRev },
|
|
} = await this.http.getJson(latestRevisionUrl, ConanRevisionJSON);
|
|
|
|
const [user, channel] = conanPackage.userAndChannel.split('/');
|
|
const packageUrl = joinUrlParts(
|
|
`${groups.host}/artifactory/api/storage/${groups.repo}`,
|
|
`${user}/${conanPackage.conanName}/${latestVersion}/${channel}/${packageRev}/export/conanfile.py?properties=conan.package.url`,
|
|
);
|
|
const { body: conanProperties } = await this.http.getJson(
|
|
packageUrl,
|
|
ConanProperties,
|
|
);
|
|
const { sourceUrl } = conanProperties;
|
|
if (sourceUrl) {
|
|
dep.sourceUrl = sourceUrl;
|
|
}
|
|
}
|
|
} catch (err) {
|
|
logger.debug({ err }, "Couldn't determine Conan package url");
|
|
}
|
|
return dep;
|
|
}
|
|
} catch (err) {
|
|
this.handleGenericErrors(err);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
getReleases(config: GetReleasesConfig): Promise<ReleaseResult | null> {
|
|
return withCache(
|
|
{
|
|
namespace: `datasource-${datasource}`,
|
|
// TODO: types (#22198)
|
|
key: `getReleases:${config.registryUrl}:${config.packageName}`,
|
|
fallback: true,
|
|
},
|
|
() => this._getReleases(config),
|
|
);
|
|
}
|
|
}
|