Skip to content
Snippets Groups Projects
Unverified Commit 44fc39b8 authored by Sergei Zharinov's avatar Sergei Zharinov Committed by GitHub
Browse files

feat(conan): Use schema for datasource (#33528)

parent 5df1b835
No related branches found
No related tags found
No related merge requests found
......@@ -6,7 +6,7 @@ export const defaultRegistryUrl = 'https://center2.conan.io/';
export const datasource = 'conan';
export const conanDatasourceRegex = regEx(
/(?<name>[a-zA-Z\-_0-9]+)\/(?<version>[^@/\n]+)(?<userChannel>@\S+\/\S+)/gim,
/^(?<name>[a-zA-Z\-_0-9]+)\/(?<version>[^@/\n]+)(?<userChannel>@\S+\/\S+)$/im,
);
export function getConanPackage(packageName: string): ConanPackage {
......
......@@ -360,6 +360,7 @@ describe('modules/datasource/conan/index', () => {
version: '1.1.1',
},
],
sourceUrl: 'https://fake.conan.url.com',
});
});
......
......@@ -3,7 +3,6 @@ import { logger } from '../../../logger';
import { cache } from '../../../util/cache/package/decorator';
import { GithubHttp } from '../../../util/http/github';
import { ensureTrailingSlash, joinUrlParts } from '../../../util/url';
import { parseSingleYaml } from '../../../util/yaml';
import * as allVersioning from '../../versioning';
import { Datasource } from '../datasource';
import type {
......@@ -13,19 +12,14 @@ import type {
ReleaseResult,
} from '../types';
import { isArtifactoryServer } from '../util';
import { datasource, defaultRegistryUrl, getConanPackage } from './common';
import {
conanDatasourceRegex,
datasource,
defaultRegistryUrl,
getConanPackage,
} from './common';
import type {
ConanCenterReleases,
ConanJSON,
ConanLatestRevision,
ConanProperties,
ConanRevisionJSON,
ConanRevisionsJSON,
ConanYAML,
} from './types';
} from './schema';
export class ConanDatasource extends Datasource {
static readonly id = datasource;
......@@ -62,13 +56,7 @@ export class ConanDatasource extends Datasource {
const res = await this.githubHttp.get(url, {
headers: { accept: 'application/vnd.github.v3.raw' },
});
// TODO: use schema (#9610)
const doc = parseSingleYaml<ConanYAML>(res.body);
return {
releases: Object.keys(doc?.versions ?? {}).map((version) => ({
version,
})),
};
return ConanCenterReleases.parse(res.body);
}
@cache({
......@@ -94,10 +82,11 @@ export class ConanDatasource extends Datasource {
conanPackage.userAndChannel,
'/revisions',
);
const revisionRep =
await this.http.getJson<ConanRevisionsJSON>(revisionLookUp);
const revisions = revisionRep?.body.revisions;
return revisions?.[0].revision ?? null;
const { body: digest } = await this.http.getJson(
revisionLookUp,
ConanLatestRevision,
);
return digest;
}
@cache({
......@@ -135,25 +124,16 @@ export class ConanDatasource extends Datasource {
);
try {
const rep = await this.http.getJson<ConanJSON>(lookupUrl);
const versions = rep?.body;
if (versions) {
const rep = await this.http.getJson(lookupUrl);
const conanJson = ConanJSON.parse(rep.body);
if (conanJson) {
logger.trace({ lookupUrl }, 'Got conan api result');
const dep: ReleaseResult = { releases: [] };
for (const resultString of Object.values(versions.results ?? {})) {
conanDatasourceRegex.lastIndex = 0;
const fromMatch = conanDatasourceRegex.exec(resultString);
if (fromMatch?.groups?.version && fromMatch?.groups?.userChannel) {
const version = fromMatch.groups.version;
if (fromMatch.groups.userChannel === userAndChannel) {
const result: Release = {
version,
};
dep.releases.push(result);
}
}
}
const conanJsonReleases: Release[] = conanJson
.filter(({ userChannel }) => userChannel === userAndChannel)
.map(({ version }) => ({ version }));
dep.releases.push(...conanJsonReleases);
try {
if (isArtifactoryServer(rep)) {
......@@ -182,25 +162,22 @@ export class ConanDatasource extends Datasource {
url,
`v2/conans/${conanPackage.conanName}/${latestVersion}/${conanPackage.userAndChannel}/latest`,
);
const revResp =
await this.http.getJson<ConanRevisionJSON>(latestRevisionUrl);
const packageRev = revResp.body.revision;
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 packageUrlResp =
await this.http.getJson<ConanProperties>(packageUrl);
if (
packageUrlResp.body.properties &&
'conan.package.url' in packageUrlResp.body.properties
) {
const conanPackageUrl =
packageUrlResp.body.properties['conan.package.url'][0];
dep.sourceUrl = conanPackageUrl;
const { body: conanProperties } = await this.http.getJson(
packageUrl,
ConanProperties,
);
const { sourceUrl } = conanProperties;
if (sourceUrl) {
dep.sourceUrl = sourceUrl;
}
}
} catch (err) {
......
import { z } from 'zod';
import { LooseArray, Yaml } from '../../../util/schema-utils';
import type { ReleaseResult } from '../types';
import { conanDatasourceRegex } from './common';
export const ConanCenterReleases = Yaml.pipe(
z.object({
versions: z.record(z.string(), z.unknown()),
}),
)
.transform(
({ versions }): ReleaseResult => ({
releases: Object.keys(versions).map((version) => ({ version })),
}),
)
.nullable()
.catch(null);
export const ConanJSON = z
.object({
results: z
.string()
.array()
.transform((array) =>
array.map((val) => val.match(conanDatasourceRegex)?.groups),
)
.pipe(
LooseArray(
z.object({
name: z.string(),
version: z.string(),
userChannel: z.string(),
}),
),
),
})
.transform(({ results }) => results)
.nullable()
.catch(null);
export const ConanRevisionJSON = z.object({
revision: z.string(),
time: z.string(),
});
export const ConanLatestRevision = z
.object({ revisions: z.unknown().array() })
.transform(({ revisions }) => revisions[0])
.pipe(ConanRevisionJSON)
.transform(({ revision }) => revision)
.nullable()
.catch(null);
export const ConanProperties = z
.object({
properties: z.object({
'conan.package.url': z.union([
z.string().transform((url) => [url]),
z.string().array(),
]),
}),
})
.transform(({ properties }) => {
const sourceUrl = properties['conan.package.url'][0];
return { sourceUrl };
});
export interface ConanJSON {
results?: Record<string, string>;
}
export interface ConanRevisionJSON {
revision: string;
time: string;
}
export interface ConanRevisionsJSON {
revisions?: Record<string, ConanRevisionJSON>;
}
export interface ConanYAML {
versions?: Record<string, unknown>;
}
export interface ConanPackage {
conanName: string;
userAndChannel: string;
}
export interface ConanRecipeProperties {
'conan.package.channel': string[];
'conan.package.license': string[];
'conan.package.name': string[];
'conan.package.url': string[];
'conan.package.user': string[];
'conan.package.version': string[];
}
export interface ConanProperties {
properties: ConanRecipeProperties;
uri: string;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment