Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(conan): add revisions support #16871

Merged
merged 17 commits into from Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 51 additions & 0 deletions lib/modules/datasource/conan/__fixtures__/poco_revisions.json
@@ -0,0 +1,51 @@
{
"1.10.1": {
"reference": "poco/1.10.1@_/_",
"revisions": [
{
"revision": "32815db5b18046e4f8879af7bb743422",
segretil marked this conversation as resolved.
Show resolved Hide resolved
"time": "2022-07-18T09:38:37.372+0000"
}
]
},
"1.10.0": {
"reference": "poco/1.10.0@_/_",
"revisions": [
{
"revision": "7c3fbb12b69d7e7fd7134ba1efb04d5e",
"time": "2022-07-18T09:38:37.768+0000"
}
]
},
"1.9.4": {
"reference": "poco/1.9.4@_/_",
"revisions": [
{
"revision": "736fdb96cd0add0cf85ca420e0bac453",
"time": "2022-07-18T09:38:38.396+0000"
}
]
},
"1.9.3": {
"reference": "poco/1.9.3@_/_",
"revisions": [
{
"revision": "dc80dcf02da270cc5f629c00df151a56",
"time": "2022-07-18T09:38:38.975+0000"
},
{
"revision": "92b4e6cf83f6be0b9589ba61cc4a5b4e",
"time": "2022-07-11T16:37:30.926+0000"
}
]
},
"1.8.1": {
"reference": "poco/1.8.1@_/_",
"revisions": [
{
"revision": "3a9b47caee2e2c1d3fb7d97788339aa8",
"time": "2022-07-18T09:38:38.815+0000"
}
]
}
}
@@ -0,0 +1,45 @@
{
"1.10.1": {
"reference": "poco/1.10.1@_/_"
},
"1.10.0": {
"reference": "poco/1.10.0@_/_",
"revisions": [
{
"revision": "7c3fbb12b69d7e7fd7134ba1efb04d5e",
segretil marked this conversation as resolved.
Show resolved Hide resolved
"time": "2022-07-18T09:38:37.768+0000"
}
]
},
"1.9.4": {
"reference": "poco/1.9.4@_/_",
"revisions": [
{
"revision": "736fdb96cd0add0cf85ca420e0bac453",
"time": "2022-07-18T09:38:38.396+0000"
}
]
},
"1.9.3": {
"reference": "poco/1.9.3@_/_",
"revisions": [
{
"revision": "dc80dcf02da270cc5f629c00df151a56",
"time": "2022-07-18T09:38:38.975+0000"
},
{
"revision": "92b4e6cf83f6be0b9589ba61cc4a5b4e",
"time": "2022-07-11T16:37:30.926+0000"
}
]
},
"1.8.1": {
"reference": "poco/1.8.1@_/_",
"revisions": [
{
"revision": "3a9b47caee2e2c1d3fb7d97788339aa8",
"time": "2022-07-18T09:38:38.815+0000"
}
]
}
}
33 changes: 30 additions & 3 deletions lib/modules/datasource/conan/index.spec.ts
@@ -1,12 +1,13 @@
import { getPkgReleases } from '..';
import { getDigest, getPkgReleases } from '..';
import { Fixtures } from '../../../../test/fixtures';
import * as httpMock from '../../../../test/http-mock';
import * as conan from '../../versioning/conan';
import type { GetPkgReleasesConfig } from '../types';
import type { GetDigestInputConfig, GetPkgReleasesConfig } from '../types';
import { defaultRegistryUrl } from './common';
import { ConanDatasource } from '.';

const pocoJson = Fixtures.get('poco.json');
const pocoRevisions = Fixtures.getJson('poco_revisions.json');
const pocoYamlGitHubContent = Fixtures.get('poco.yaml');
const malformedJson = Fixtures.get('malformed.json');
const fakeJson = Fixtures.get('fake.json');
Expand All @@ -21,11 +22,37 @@ const config: GetPkgReleasesConfig = {
registryUrls: [nonDefaultRegistryUrl],
};

const digestConfig: GetDigestInputConfig = {
depName: 'fake',
datasource,
registryUrls: [nonDefaultRegistryUrl],
};

describe('modules/datasource/conan/index', () => {
beforeEach(() => {
config.registryUrls = [nonDefaultRegistryUrl];
});

describe('getDigest', () => {
it('handles package without digest', async () => {
digestConfig.packageName = 'fakepackage/1.2@_/_';
expect(await getDigest(digestConfig)).toBeNull();
});

it('handles digest', async () => {
const version = '1.8.1';
httpMock
.scope(nonDefaultRegistryUrl)
.get(`/v2/conans/poco/${version}/_/_/revisions`)
.reply(200, pocoRevisions['1.8.1']);
digestConfig.packageName = `poco/${version}@_/_`;
digestConfig.currentDigest = '4fc13d60fd91ba44fefe808ad719a5af';
expect(await getDigest(digestConfig)).toBe(
'3a9b47caee2e2c1d3fb7d97788339aa8'
);
});
});

describe('getReleases', () => {
it('handles bad return', async () => {
httpMock
Expand Down Expand Up @@ -117,7 +144,7 @@ describe('modules/datasource/conan/index', () => {
});
});

it('uses github isntead of conan center', async () => {
it('uses github instead of conan center', async () => {
httpMock
.scope('https://api.github.com')
.get(
Expand Down
41 changes: 39 additions & 2 deletions lib/modules/datasource/conan/index.ts
Expand Up @@ -5,9 +5,14 @@ import { cache } from '../../../util/cache/package/decorator';
import { GithubHttp } from '../../../util/http/github';
import { ensureTrailingSlash, joinUrlParts } from '../../../util/url';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, Release, ReleaseResult } from '../types';
import type {
DigestConfig,
GetReleasesConfig,
Release,
ReleaseResult,
} from '../types';
import { conanDatasourceRegex, datasource, defaultRegistryUrl } from './common';
import type { ConanJSON, ConanYAML } from './types';
import type { ConanJSON, ConanRevisionsJSON, ConanYAML } from './types';

export class ConanDatasource extends Datasource {
static readonly id = datasource;
Expand Down Expand Up @@ -50,6 +55,38 @@ export class ConanDatasource extends Datasource {
};
}

@cache({
namespace: `datasource-${datasource}-revisions`,
key: ({ registryUrl, packageName }: GetReleasesConfig) =>
// TODO: types (#7154)
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${registryUrl}:${packageName}`,
segretil marked this conversation as resolved.
Show resolved Hide resolved
})
override async getDigest(
{ registryUrl, packageName, currentDigest }: DigestConfig,
newValue?: string
): Promise<string | null> {
if (is.undefined(currentDigest) || is.undefined(registryUrl)) {
segretil marked this conversation as resolved.
Show resolved Hide resolved
return null;
} else {
segretil marked this conversation as resolved.
Show resolved Hide resolved
const url = ensureTrailingSlash(registryUrl);
const depName = packageName.split('/')[0];
const userAndChannel = '/' + packageName.split('@')[1];
const packageRoute = newValue
? `${depName}/${newValue}${userAndChannel}`
: packageName.replace('@', '/');
segretil marked this conversation as resolved.
Show resolved Hide resolved
const revisionLookUp = joinUrlParts(
url,
`v2/conans/${packageRoute}/revisions`
);
const revisionRep = await this.http.getJson<ConanRevisionsJSON>(
revisionLookUp
);
const revisions = revisionRep?.body.revisions;
return revisions ? revisions[0].revision : null;
}
}

@cache({
namespace: `datasource-${datasource}`,
key: ({ registryUrl, packageName }: GetReleasesConfig) =>
Expand Down
9 changes: 9 additions & 0 deletions lib/modules/datasource/conan/types.ts
Expand Up @@ -2,6 +2,15 @@ export interface ConanJSON {
results?: Record<string, string>;
}

export interface ConanRevisionJSON {
revision: string;
time: string
}

export interface ConanRevisionsJSON {
segretil marked this conversation as resolved.
Show resolved Hide resolved
revisions?: Record<string, ConanRevisionJSON>;
}

export interface ConanYAML {
versions?: Record<string, unknown>;
}
4 changes: 4 additions & 0 deletions lib/modules/manager/conan/__fixtures__/conanfile.py
Expand Up @@ -12,6 +12,10 @@ class Pkg(ConanFile):
requires = (("req_c/1.0@user/stable", "private"), )
requires = ("req_f/1.0@user/stable", ("req_h/3.0@other/beta", "override"))
requires = "req_g/[>1.0 <1.8]@user/stable"
# requires = "commentedout/[>1.0 <1.8]@user/stable"
# requires = "commentedout2/[>1.0 <1.8]@user/stable"
requires = (("req_l/1.0@user/stable#bc592346b33fd19c1fbffce25d1e4236", "private"), )



def requirements(self):
Expand Down
2 changes: 2 additions & 0 deletions lib/modules/manager/conan/__fixtures__/conanfile.txt
Expand Up @@ -2,6 +2,7 @@
poco/1.9.4
zlib/[~1.2.3, loose=False]
fake/8.62.134@test/dev
cairo/1.17.2@_/_#aff2d03608351db075ec1348a3afc9ff

[build_requires]
7zip/[>1.1 <2.1, include_prerelease=True]
Expand All @@ -13,6 +14,7 @@ cmake/[>1.1 || 0.8]
cryptopp/[1.2.7 || >=1.2.9 <2.0.0]@test/local
#commentedout/1.2
# commentedout/3.4
meson/0.63.0@_/_#bc592346b33fd19c1fbffce25d1e4236

[generators]
xcode
Expand Down
3 changes: 3 additions & 0 deletions lib/modules/manager/conan/common.ts
@@ -0,0 +1,3 @@
export function isComment(line: string): boolean {
return line.trim().startsWith('#');
segretil marked this conversation as resolved.
Show resolved Hide resolved
}
31 changes: 31 additions & 0 deletions lib/modules/manager/conan/extract.spec.ts
Expand Up @@ -35,6 +35,16 @@ describe('modules/manager/conan/extract', () => {
packageName: 'fake/8.62.134@test/dev',
replaceString: 'fake/8.62.134@test/dev',
},
{
autoReplaceStringTemplate:
'{{depName}}/{{newValue}}@_/_{{#if newDigest}}#{{newDigest}}{{/if}}',
currentDigest: 'aff2d03608351db075ec1348a3afc9ff',
currentValue: '1.17.2',
depName: 'cairo',
depType: 'requires',
packageName: 'cairo/1.17.2@_/_',
replaceString: 'cairo/1.17.2@_/_#aff2d03608351db075ec1348a3afc9ff',
},
{
currentValue: '[>1.1 <2.1, include_prerelease=True]',
depName: '7zip',
Expand Down Expand Up @@ -86,6 +96,16 @@ describe('modules/manager/conan/extract', () => {
packageName: 'cryptopp/[1.2.7 || >=1.2.9 <2.0.0]@test/local',
replaceString: 'cryptopp/[1.2.7 || >=1.2.9 <2.0.0]@test/local',
},
{
autoReplaceStringTemplate:
'{{depName}}/{{newValue}}@_/_{{#if newDigest}}#{{newDigest}}{{/if}}',
currentDigest: 'bc592346b33fd19c1fbffce25d1e4236',
currentValue: '0.63.0',
depName: 'meson',
depType: 'build_requires',
packageName: 'meson/0.63.0@_/_',
replaceString: 'meson/0.63.0@_/_#bc592346b33fd19c1fbffce25d1e4236',
},
]);
});

Expand Down Expand Up @@ -181,6 +201,17 @@ describe('modules/manager/conan/extract', () => {
packageName: 'req_g/[>1.0 <1.8]@user/stable',
replaceString: 'req_g/[>1.0 <1.8]@user/stable',
},
{
autoReplaceStringTemplate:
'{{depName}}/{{newValue}}@user/stable{{#if newDigest}}#{{newDigest}}{{/if}}',
currentDigest: 'bc592346b33fd19c1fbffce25d1e4236',
currentValue: '1.0',
depName: 'req_l',
depType: 'requires',
packageName: 'req_l/1.0@user/stable',
replaceString:
'req_l/1.0@user/stable#bc592346b33fd19c1fbffce25d1e4236',
},
{
currentValue: '1.2',
depName: 'req_i',
Expand Down
19 changes: 12 additions & 7 deletions lib/modules/manager/conan/extract.ts
@@ -1,9 +1,10 @@
import is from '@sindresorhus/is';
import { regEx } from '../../../util/regex';
import type { PackageDependency, PackageFile } from '../types';
import { isComment } from './common';

const regex = regEx(
`(?<name>[-_a-z0-9]+)/(?<version>[^@\n{*"']+)(?<userChannel>@[-_a-zA-Z0-9]+/[^\n.{*"' ]+)?`
`(?<name>[-_a-z0-9]+)/(?<version>[^@\n{*"']+)(?<userChannel>@[-_a-zA-Z0-9]+/[^#\n.{*"' ]+)?#?(?<revision>[-_a-f0-9]+[^\n{*"'])?`
);

function setDepType(content: string, originalType: string): string {
Expand Down Expand Up @@ -32,13 +33,11 @@ export function extractPackageFile(content: string): PackageFile | null {
let depType = setDepType(section, 'requires');
const rawLines = section.split('\n').filter(is.nonEmptyString);

for (const rawline of rawLines) {
// don't process after a comment
const sanitizedLine = rawline.split('#')[0].split('//')[0];
if (sanitizedLine) {
depType = setDepType(sanitizedLine, depType);
for (const rawLine of rawLines) {
if (!isComment(rawLine)) {
depType = setDepType(rawLine, depType);
// extract all dependencies from each line
const lines = sanitizedLine.split(/["'],/);
const lines = rawLine.split(/["'],/);
for (const line of lines) {
const matches = regex.exec(line.trim());
if (matches?.groups) {
Expand All @@ -64,6 +63,12 @@ export function extractPackageFile(content: string): PackageFile | null {
replaceString,
depType,
};
if (matches.groups.revision) {
dep.currentDigest = matches.groups.revision;
dep.autoReplaceStringTemplate = `{{depName}}/{{newValue}}${userAndChannel}{{#if newDigest}}#{{newDigest}}{{/if}}`;
dep.replaceString = `${replaceString}#${dep.currentDigest}`;
}

deps.push(dep);
}
}
Expand Down