diff --git a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap index 851c577da6272818981628d1b83a4dd323cda7de..7dc46ba59b946719f6d7680902537f8720cd76fe 100644 --- a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap @@ -92,6 +92,7 @@ Object { exports[`datasource/pypi getReleases processes real data 1`] = ` Object { + "homepage": "https://github.com/Azure/azure-cli", "releases": Array [ Object { "releaseTimestamp": "2017-04-03T16:55:14", @@ -205,6 +206,14 @@ Object { } `; +exports[`datasource/pypi getReleases return sourceUrl in project_urls 1`] = ` +Object { + "homepage": "https://flexget.com", + "releases": Array [], + "sourceUrl": "https://github.com/Flexget/Flexget", +} +`; + exports[`datasource/pypi getReleases returns non-github home_page 1`] = ` Object { "homepage": "https://microsoft.com", diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts index 739c6264bd6757449e055f9fbffb21ccb4ab4784..4e13bea7273a65f53ee02d7ac28a1eedec11b906 100644 --- a/lib/datasource/pypi/index.spec.ts +++ b/lib/datasource/pypi/index.spec.ts @@ -122,6 +122,27 @@ describe('datasource/pypi', () => { }) ).toMatchSnapshot(); }); + it('return sourceUrl in project_urls', async () => { + got.mockReturnValueOnce({ + body: { + info: { + name: 'flexget', + home_page: 'https://flexget.com', + project_urls: { + Forum: 'https://discuss.flexget.com', + Homepage: 'https://flexget.com', + 'Issue Tracker': 'https://github.com/Flexget/Flexget/issues', + Repository: 'https://github.com/Flexget/Flexget', + }, + }, + }, + }); + const result = await pypi.getReleases({ + lookupName: 'flexget', + }); + expect(result.sourceUrl).toBe('https://github.com/Flexget/Flexget'); + expect(result).toMatchSnapshot(); + }); it('returns null if mismatched name', async () => { got.mockReturnValueOnce({ body: { diff --git a/lib/datasource/pypi/index.ts b/lib/datasource/pypi/index.ts index f85da2b359b810b79694c96de20f85e74b4799dd..42323e6dd55d4ea0f6ac6c490553af001805ac72 100644 --- a/lib/datasource/pypi/index.ts +++ b/lib/datasource/pypi/index.ts @@ -8,15 +8,29 @@ import * as pep440 from '../../versioning/pep440'; import { GetReleasesConfig, ReleaseResult } from '../common'; export const id = 'pypi'; - +const github_repo_pattern = /^https?:\/\/github\.com\/[^\\/]+\/[^\\/]+$/; const http = new Http(id); +type Releases = Record< + string, + { requires_python?: boolean; upload_time?: string }[] +>; +type PypiJSON = { + info: { + name: string; + home_page?: string; + project_urls?: Record<string, string>; + }; + + releases?: Releases; +}; + function normalizeName(input: string): string { return input.toLowerCase().replace(/(-|\.)/g, '_'); } function compatibleVersions( - releases: Record<string, { requires_python?: boolean }[]>, + releases: Releases, compatibility: Record<string, string> ): string[] { const versions = Object.keys(releases); @@ -42,8 +56,7 @@ async function getDependency( const lookupUrl = url.resolve(hostUrl, `${packageName}/json`); const dependency: ReleaseResult = { releases: null }; logger.trace({ lookupUrl }, 'Pypi api got lookup'); - // TODO: fix type - const rep = await http.getJson<any>(lookupUrl); + const rep = await http.getJson<PypiJSON>(lookupUrl); const dep = rep && rep.body; if (!dep) { logger.trace({ dependency: packageName }, 'pip package not found'); @@ -59,16 +72,31 @@ async function getDependency( ); return null; } - if (dep.info && dep.info.home_page) { - if (dep.info.home_page.match(/^https?:\/\/github.com/)) { + + if (dep.info?.home_page) { + dependency.homepage = dep.info.home_page; + if (github_repo_pattern.exec(dep.info.home_page)) { dependency.sourceUrl = dep.info.home_page.replace( 'http://', 'https://' ); - } else { - dependency.homepage = dep.info.home_page; } } + + if (dep.info?.project_urls && !dependency.sourceUrl) { + for (const [name, projectUrl] of Object.entries(dep.info.project_urls)) { + const lower = name.toLowerCase(); + if ( + lower.startsWith('repo') || + lower === 'code' || + github_repo_pattern.exec(projectUrl) + ) { + dependency.sourceUrl = projectUrl; + break; + } + } + } + dependency.releases = []; if (dep.releases) { const versions = compatibleVersions(dep.releases, compatibility);