Skip to content
Snippets Groups Projects
Unverified Commit 1c924059 authored by Pascal Mathis's avatar Pascal Mathis Committed by GitHub
Browse files

fix(repology): handle multi-package projects (#6489)

parent 733eb547
No related branches found
Tags 37.172.0
No related merge requests found
Source diff could not be displayed: it is too large. Options to address this: view the blob.
This diff is collapsed.
...@@ -25,6 +25,56 @@ Array [ ...@@ -25,6 +25,56 @@ Array [
] ]
`; `;
exports[`datasource/repology/index getReleases returns correct version for multi-package project with different name 1`] = `
Object {
"releases": Array [
Object {
"version": "12.2-4+deb10u1",
},
],
}
`;
exports[`datasource/repology/index getReleases returns correct version for multi-package project with different name 2`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"host": "repology.org",
"user-agent": "https://github.com/renovatebot/renovate",
},
"method": "GET",
"url": "https://repology.org/tools/project-by?repo=debian_stable&name_type=binname&target_page=api_v1_project&noautoresolve=on&name=pulseaudio-utils",
},
]
`;
exports[`datasource/repology/index getReleases returns correct version for multi-package project with same name 1`] = `
Object {
"releases": Array [
Object {
"version": "9.3.0-r2",
},
],
}
`;
exports[`datasource/repology/index getReleases returns correct version for multi-package project with same name 2`] = `
Array [
Object {
"headers": Object {
"accept": "application/json",
"accept-encoding": "gzip, deflate",
"host": "repology.org",
"user-agent": "https://github.com/renovatebot/renovate",
},
"method": "GET",
"url": "https://repology.org/tools/project-by?repo=alpine_3_12&name_type=binname&target_page=api_v1_project&noautoresolve=on&name=gcc",
},
]
`;
exports[`datasource/repology/index getReleases returns correct version for source package 1`] = ` exports[`datasource/repology/index getReleases returns correct version for source package 1`] = `
Object { Object {
"releases": Array [ "releases": Array [
......
...@@ -3,6 +3,7 @@ import * as httpMock from '../../../test/httpMock'; ...@@ -3,6 +3,7 @@ import * as httpMock from '../../../test/httpMock';
import { getName } from '../../../test/util'; import { getName } from '../../../test/util';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { DATASOURCE_FAILURE } from '../../constants/error-messages';
import * as ds from '.'; import * as ds from '.';
import { RepologyPackage } from '.';
const repologyApiHost = 'https://repology.org/'; const repologyApiHost = 'https://repology.org/';
...@@ -45,6 +46,14 @@ const fixtureGccDefaults = fs.readFileSync( ...@@ -45,6 +46,14 @@ const fixtureGccDefaults = fs.readFileSync(
`${__dirname}/__fixtures__/gcc-defaults.json`, `${__dirname}/__fixtures__/gcc-defaults.json`,
'utf8' 'utf8'
); );
const fixtureGcc = fs.readFileSync(
`${__dirname}/__fixtures__/gcc.json`,
'utf8'
);
const fixturePulseaudio = fs.readFileSync(
`${__dirname}/__fixtures__/pulseaudio.json`,
'utf8'
);
describe(getName(__filename), () => { describe(getName(__filename), () => {
describe('getReleases', () => { describe('getReleases', () => {
...@@ -132,7 +141,7 @@ describe(getName(__filename), () => { ...@@ -132,7 +141,7 @@ describe(getName(__filename), () => {
const res = await ds.getReleases({ lookupName: 'debian_stable/nginx' }); const res = await ds.getReleases({ lookupName: 'debian_stable/nginx' });
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(1); expect(res.releases).toHaveLength(1);
expect(res.releases[0].version).toBeString(); expect(res.releases[0].version).toEqual('1.14.2-2+deb10u1');
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
...@@ -149,8 +158,57 @@ describe(getName(__filename), () => { ...@@ -149,8 +158,57 @@ describe(getName(__filename), () => {
}); });
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(1); expect(res.releases).toHaveLength(1);
expect(res.releases[0].version).toBeString(); expect(res.releases[0].version).toEqual('1.181');
expect(httpMock.getTrace()).toMatchSnapshot();
});
it('returns correct version for multi-package project with same name', async () => {
mockProjectBy(
'alpine_3_12',
'gcc',
{ status: 200, body: fixtureGcc },
null
);
const res = await ds.getReleases({ lookupName: 'alpine_3_12/gcc' });
expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(1);
expect(res.releases[0].version).toEqual('9.3.0-r2');
expect(httpMock.getTrace()).toMatchSnapshot(); expect(httpMock.getTrace()).toMatchSnapshot();
}); });
it('returns correct version for multi-package project with different name', async () => {
mockProjectBy(
'debian_stable',
'pulseaudio-utils',
{ status: 200, body: fixturePulseaudio },
null
);
const res = await ds.getReleases({
lookupName: 'debian_stable/pulseaudio-utils',
});
expect(res).toMatchSnapshot();
expect(res.releases).toHaveLength(1);
expect(res.releases[0].version).toEqual('12.2-4+deb10u1');
expect(httpMock.getTrace()).toMatchSnapshot();
});
it('returns null for ambiguous package results', async () => {
const pkgs: RepologyPackage[] = [
{ repo: 'dummy', version: '1.0.0', visiblename: 'example' },
{ repo: 'dummy', version: '2.0.0', visiblename: 'example' },
];
const pkgsJSON = JSON.stringify(pkgs);
mockProjectBy(
'dummy',
'example',
{ status: 200, body: pkgsJSON },
{ status: 200, body: pkgsJSON }
);
expect(await ds.getReleases({ lookupName: 'dummy/example' })).toBeNull();
});
}); });
}); });
...@@ -16,7 +16,9 @@ export interface RepologyPackage { ...@@ -16,7 +16,9 @@ export interface RepologyPackage {
repo: string; repo: string;
visiblename: string; visiblename: string;
version: string; version: string;
origversion: string | null; srcname?: string;
binname?: string;
origversion?: string;
} }
async function queryPackage( async function queryPackage(
...@@ -33,12 +35,30 @@ async function queryPackage( ...@@ -33,12 +35,30 @@ async function queryPackage(
name: pkgName, name: pkgName,
}).toString(); }).toString();
// Retrieve list of packages by looking up Repology project
const url = `https://repology.org/tools/project-by?${query}`; const url = `https://repology.org/tools/project-by?${query}`;
const res = await http.getJson<RepologyPackage[]>(url); const res = await http.getJson<RepologyPackage[]>(url);
let pkgs = res.body.filter((pkg) => pkg.repo === repoName);
return res.body.find( // In some cases Repology bundles multiple packages into a single project,
(x) => x.repo.toLowerCase() === repoName.toLowerCase() // which would result in ambiguous results. If we have more than one result
); // left, we should try to determine the correct package by comparing either
// binname or srcname (depending on pkgType) to the given dependency name.
if (pkgs.length > 1) {
pkgs = pkgs.filter((pkg) => !pkg[pkgType] || pkg[pkgType] === pkgName);
}
// Abort if there is still more than one package left, as the result would
// be ambiguous and unreliable. This should usually not happen...
if (pkgs.length > 1) {
logger.warn(
{ repoName, pkgName, pkgType, pkgs },
'Repology lookup returned ambiguous results, ignoring...'
);
return null;
}
return pkgs[0];
} catch (err) { } catch (err) {
if (err.statusCode === 404) { if (err.statusCode === 404) {
logger.debug( logger.debug(
......
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