implement first batch of URL results for sources

This commit is contained in:
Daniel Peukert 2023-10-18 02:00:46 +02:00
parent 5a6fee2817
commit 6bf34873d3
No known key found for this signature in database
17 changed files with 162 additions and 44 deletions

View file

@ -1,10 +1,15 @@
# MIT licensed # MIT licensed
# Copyright (c) 2017-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2017-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
URL = 'https://release-monitoring.org/api/project/{pkg}' URL = 'https://release-monitoring.org/api/project/{pkg}'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
pkg = conf.get('anitya') pkg = conf.get('anitya')
url = URL.format(pkg = pkg) url = URL.format(pkg = pkg)
data = await cache.get_json(url) data = await cache.get_json(url)
return data['version'] return RichResult(
version = data['version'],
url = f'https://release-monitoring.org/project/{data["id"]}/',
)

View file

@ -11,8 +11,8 @@ import functools
from collections import defaultdict from collections import defaultdict
from nvchecker.api import ( from nvchecker.api import (
session, GetVersionError, session, GetVersionError, VersionResult,
VersionResult, Entry, AsyncCache, KeyManager, RichResult, Entry, AsyncCache, KeyManager,
) )
APT_RELEASE_URL = "%s/dists/%s/Release" APT_RELEASE_URL = "%s/dists/%s/Release"
@ -92,12 +92,13 @@ async def get_url(url: str) -> str:
None, _decompress_data, None, _decompress_data,
url, data) url, data)
async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], Dict[str, str]]: async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], Dict[str, str], Dict[str, str]]:
cache, url = key cache, url = key
apt_packages = await cache.get(url, get_url) # type: ignore apt_packages = await cache.get(url, get_url) # type: ignore
pkg_map = defaultdict(list) pkg_map = defaultdict(list)
srcpkg_map = defaultdict(list) srcpkg_map = defaultdict(list)
pkg_to_src_map = defaultdict(list)
pkg = None pkg = None
srcpkg = None srcpkg = None
@ -110,6 +111,7 @@ async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], D
version = line[9:] version = line[9:]
if pkg is not None: if pkg is not None:
pkg_map[pkg].append(version) pkg_map[pkg].append(version)
pkg_to_src_map["%s/%s" % (pkg, version)] = srcpkg if srcpkg is not None else pkg
if srcpkg is not None: if srcpkg is not None:
srcpkg_map[srcpkg].append(version) srcpkg_map[srcpkg].append(version)
pkg = srcpkg = None pkg = srcpkg = None
@ -118,8 +120,10 @@ async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], D
for pkg, vs in pkg_map.items()} for pkg, vs in pkg_map.items()}
srcpkg_map_max = {pkg: max(vs, key=functools.cmp_to_key(compare_version)) srcpkg_map_max = {pkg: max(vs, key=functools.cmp_to_key(compare_version))
for pkg, vs in srcpkg_map.items()} for pkg, vs in srcpkg_map.items()}
pkg_to_src_map_max = {pkg: pkg_to_src_map["%s/%s" % (pkg, vs)]
for pkg, vs in pkg_map_max.items()}
return pkg_map_max, srcpkg_map_max return pkg_map_max, srcpkg_map_max, pkg_to_src_map_max
async def get_version( async def get_version(
name: str, conf: Entry, *, name: str, conf: Entry, *,
@ -148,16 +152,38 @@ async def get_version(
else: else:
raise GetVersionError('Packages file not found in APT repository') raise GetVersionError('Packages file not found in APT repository')
pkg_map, srcpkg_map = await cache.get( pkg_map, srcpkg_map, pkg_to_src_map = await cache.get(
(cache, APT_PACKAGES_URL % (mirror, suite, packages_path)), parse_packages) # type: ignore (cache, APT_PACKAGES_URL % (mirror, suite, packages_path)), parse_packages) # type: ignore
if pkg and pkg in pkg_map: if pkg and pkg in pkg_map:
version = pkg_map[pkg] version = pkg_map[pkg]
changelog_name = pkg_to_src_map[pkg]
elif srcpkg and srcpkg in srcpkg_map: elif srcpkg and srcpkg in srcpkg_map:
version = srcpkg_map[srcpkg] version = srcpkg_map[srcpkg]
changelog_name = srcpkg
else: else:
raise GetVersionError('package not found in APT repository') raise GetVersionError('package not found in APT repository')
# Get Changelogs field from the Release file
changelogs_url = None
for line in apt_release.split('\n'):
if line.startswith('Changelogs: '):
changelogs_url = line[12:]
break
# Build the changelog URL (see https://wiki.debian.org/DebianRepository/Format#Changelogs for spec)
changelog = None
if changelogs_url is not None and changelogs_url != 'no':
changelog_section = changelog_name[:4] if changelog_name.startswith('lib') else changelog_name[:1]
changelog = changelogs_url.replace('@CHANGEPATH@', f'{repo}/{changelog_section}/{changelog_name}/{changelog_name}_{version}')
if strip_release: if strip_release:
version = version.split("-")[0] version = version.split("-")[0]
return version
if changelog is not None:
return RichResult(
version = version,
url = changelog,
)
else:
return version

View file

@ -1,7 +1,7 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import session, GetVersionError from nvchecker.api import session, RichResult, GetVersionError
URL = 'https://www.archlinux.org/packages/search/json/' URL = 'https://www.archlinux.org/packages/search/json/'
@ -31,4 +31,7 @@ async def get_version(name, conf, *, cache, **kwargs):
else: else:
version = r['pkgver'] + '-' + r['pkgrel'] version = r['pkgver'] + '-' + r['pkgrel']
return version return RichResult(
version = version,
url = f'https://archlinux.org/packages/{r["repo"]}/{r["arch"]}/{r["pkgname"]}/',
)

View file

@ -1,11 +1,15 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
# Using metacpan # Using metacpan
CPAN_URL = 'https://fastapi.metacpan.org/release/%s' CPAN_URL = 'https://fastapi.metacpan.org/release/%s'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
key = conf.get('cpan', name) key = conf.get('cpan', name)
data = await cache.get_json(CPAN_URL % key) data = await cache.get_json(CPAN_URL % key)
return str(data['version']) return RichResult(
version = str(data['version']),
url = f'https://metacpan.org/release/{data["author"]}/{data["name"]}',
)

View file

@ -1,7 +1,7 @@
# MIT licensed # MIT licensed
# Copyright (c) 2022 Pekka Ristola <pekkarr [at] protonmail [dot] com>, et al. # Copyright (c) 2022 Pekka Ristola <pekkarr [at] protonmail [dot] com>, et al.
from nvchecker.api import session, GetVersionError from nvchecker.api import session, RichResult, GetVersionError
CRAN_URL = 'https://cran.r-project.org/package=%s/DESCRIPTION' CRAN_URL = 'https://cran.r-project.org/package=%s/DESCRIPTION'
VERSION_FIELD = 'Version: ' VERSION_FIELD = 'Version: '
@ -23,4 +23,7 @@ async def get_version(name, conf, *, cache, **kwargs):
else: else:
raise GetVersionError('Invalid DESCRIPTION file') raise GetVersionError('Invalid DESCRIPTION file')
return version return RichResult(
version = version,
url = f'https://cran.r-project.org/web/packages/{package}/',
)

View file

@ -1,10 +1,15 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
API_URL = 'https://crates.io/api/v1/crates/%s' API_URL = 'https://crates.io/api/v1/crates/%s'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
name = conf.get('cratesio') or name name = conf.get('cratesio') or name
data = await cache.get_json(API_URL % name) data = await cache.get_json(API_URL % name)
version = [v['num'] for v in data['versions'] if not v['yanked']][0] version = [v['num'] for v in data['versions'] if not v['yanked']][0]
return version return RichResult(
version = version,
url = f'https://crates.io/crates/{name}/{version}',
)

View file

@ -2,7 +2,7 @@
# Copyright (c) 2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2020 lilydjwg <lilydjwg@gmail.com>, et al.
# Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al. # Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al.
from nvchecker.api import GetVersionError from nvchecker.api import RichResult, GetVersionError
URL = 'https://sources.debian.org/api/src/%(pkgname)s/?suite=%(suite)s' URL = 'https://sources.debian.org/api/src/%(pkgname)s/?suite=%(suite)s'
@ -22,4 +22,7 @@ async def get_version(name, conf, *, cache, **kwargs):
else: else:
version = r['version'] version = r['version']
return version return RichResult(
version = version,
url = f'https://sources.debian.org/src/{data["package"]}/{r["version"]}/',
)

View file

@ -1,9 +1,16 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
GEMS_URL = 'https://rubygems.org/api/v1/versions/%s.json' GEMS_URL = 'https://rubygems.org/api/v1/versions/%s.json'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
key = conf.get('gems', name) key = conf.get('gems', name)
data = await cache.get_json(GEMS_URL % key) data = await cache.get_json(GEMS_URL % key)
return [item['number'] for item in data] return [
RichResult(
version = item['number'],
url = f'https://rubygems.org/gems/{key}/versions/{item["number"]}',
) for item in data
]

View file

@ -1,10 +1,15 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
HACKAGE_URL = 'https://hackage.haskell.org/package/%s/preferred.json' HACKAGE_URL = 'https://hackage.haskell.org/package/%s/preferred.json'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
key = conf.get('hackage', name) key = conf.get('hackage', name)
data = await cache.get_json(HACKAGE_URL % key) data = await cache.get_json(HACKAGE_URL % key)
return data['normal-version'][0] version = data['normal-version'][0]
return RichResult(
version = version,
url = f'https://hackage.haskell.org/package/{key}-{version}',
)

View file

@ -3,7 +3,7 @@
import json import json
import re import re
from nvchecker.api import session from nvchecker.api import session, RichResult
NPM_URL = 'https://registry.npmjs.org/%s' NPM_URL = 'https://registry.npmjs.org/%s'
@ -26,4 +26,13 @@ async def get_version(name, conf, *, cache, **kwargs):
data = await cache.get(NPM_URL % key, get_first_1k) data = await cache.get(NPM_URL % key, get_first_1k)
dist_tags = json.loads(re.search(b'"dist-tags":({.*?})', data).group(1)) dist_tags = json.loads(re.search(b'"dist-tags":({.*?})', data).group(1))
return dist_tags['latest'] version = dist_tags['latest']
# There is no standardised URL scheme, so we only return an URL for the default registry
if NPM_URL.startswith('https://registry.npmjs.org/'):
return RichResult(
version = version,
url = f'https://www.npmjs.com/package/{key}/v/{version}',
)
else:
return version

View file

@ -1,6 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2021 Th3Whit3Wolf <the.white.wolf.is.1337@gmail.com>, et al. # Copyright (c) 2013-2021 Th3Whit3Wolf <the.white.wolf.is.1337@gmail.com>, et al.
from nvchecker.api import RichResult
API_URL = 'https://open-vsx.org/api/%s/%s' API_URL = 'https://open-vsx.org/api/%s/%s'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
@ -10,4 +12,7 @@ async def get_version(name, conf, *, cache, **kwargs):
extension = splitName[1] extension = splitName[1]
data = await cache.get_json(API_URL % (publisher, extension)) data = await cache.get_json(API_URL % (publisher, extension))
version = data['version'] version = data['version']
return version return RichResult(
version = version,
url = f'https://open-vsx.org/extension/{publisher}/{extension}/{version}',
)

View file

@ -1,6 +1,8 @@
# MIT licensed # MIT licensed
# Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2013-2020 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import RichResult
PACKAGIST_URL = 'https://packagist.org/packages/%s.json' PACKAGIST_URL = 'https://packagist.org/packages/%s.json'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
@ -14,4 +16,8 @@ async def get_version(name, conf, *, cache, **kwargs):
} }
if len(versions): if len(versions):
return max(versions, key=lambda version: versions[version]["time"]) version = max(versions, key=lambda version: versions[version]["time"])
return RichResult(
version = version,
url = f'https://packagist.org/packages/{data["package"]["name"]}#{version}',
)

View file

@ -6,10 +6,10 @@ import urllib.parse
import structlog import structlog
from nvchecker.api import ( from nvchecker.api import (
VersionResult, Entry, AsyncCache, KeyManager, VersionResult, RichResult, Entry, AsyncCache, KeyManager,
) )
PAGURE_URL = 'https://%s/api/0/%s/git/tags' PAGURE_URL = 'https://%s/api/0/%s/git/tags?with_commits=true'
logger = structlog.get_logger(logger_name=__name__) logger = structlog.get_logger(logger_name=__name__)
@ -24,5 +24,9 @@ async def get_version(
url = PAGURE_URL % (host, repo) url = PAGURE_URL % (host, repo)
data = await cache.get_json(url) data = await cache.get_json(url)
version = data["tags"] return [
return version RichResult(
version = version,
url = f'https://{host}/{repo}/tree/{version_hash}',
) for version, version_hash in data["tags"].items()
]

View file

@ -1,7 +1,7 @@
# MIT licensed # MIT licensed
# Copyright (c) 2019 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2019 lilydjwg <lilydjwg@gmail.com>, et al.
from nvchecker.api import GetVersionError from nvchecker.api import RichResult, GetVersionError
API_URL = 'https://repology.org/api/v1/project/{}' API_URL = 'https://repology.org/api/v1/project/{}'
@ -25,5 +25,9 @@ async def get_version(name, conf, *, cache, **kwargs):
raise GetVersionError('package is not found in subrepo', raise GetVersionError('package is not found in subrepo',
repo=repo, subrepo=subrepo) repo=repo, subrepo=subrepo)
versions = [pkg['version'] for pkg in pkgs] return [
return versions RichResult(
version = pkg['version'],
url = f'https://repology.org/project/{project}/packages',
) for pkg in pkgs
]

View file

@ -4,23 +4,25 @@
from xml.etree import ElementTree from xml.etree import ElementTree
from nvchecker.api import session from nvchecker.api import session, RichResult
NAMESPACE = 'http://www.andymatuschak.org/xml-namespaces/sparkle'
XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'
SPARKLE_NAMESPACE = 'http://www.andymatuschak.org/xml-namespaces/sparkle'
async def get_version(name, conf, *, cache, **kwargs): async def get_version(name, conf, *, cache, **kwargs):
sparkle = conf['sparkle'] sparkle = conf['sparkle']
return await cache.get(sparkle, get_version_impl) release_notes_language = conf.get('release_notes_language', 'en')
return await cache.get((sparkle, release_notes_language), get_version_impl)
async def get_version_impl(sparkle): async def get_version_impl(info):
sparkle, release_notes_language = info
res = await session.get(sparkle) res = await session.get(sparkle)
root = ElementTree.fromstring(res.body) root = ElementTree.fromstring(res.body).find('./channel/item[1]')
item = root.find('./channel/item[1]/enclosure') item = root.find('./enclosure')
version_string = item.get(f'{{{NAMESPACE}}}shortVersionString') version_string = item.get(f'{{{SPARKLE_NAMESPACE}}}shortVersionString')
build_number = item.get(f'{{{NAMESPACE}}}version') build_number = item.get(f'{{{SPARKLE_NAMESPACE}}}version')
if (version_string and version_string.isdigit()) and ( if (version_string and version_string.isdigit()) and (
build_number and not build_number.isdigit() build_number and not build_number.isdigit()
@ -34,4 +36,25 @@ async def get_version_impl(sparkle):
if build_number and (build_number not in version): if build_number and (build_number not in version):
version.append(build_number) version.append(build_number)
return '-'.join(version) if version else None version_str = '-'.join(version) if version else None
release_notes_link = None
for release_notes in root.findall(f'./{{{SPARKLE_NAMESPACE}}}releaseNotesLink'):
language = release_notes.get(f'{{{XML_NAMESPACE}}}lang')
# If the release notes have no language set, store them, but keep looking for our preferred language
if language is None:
release_notes_link = release_notes.text.strip()
# If the release notes match our preferred language, store them and stop looking
if language == release_notes_language:
release_notes_link = release_notes.text.strip()
break
if release_notes_link is not None:
return RichResult(
version = version_str,
url = release_notes_link,
)
else:
return version_str

View file

@ -2,7 +2,7 @@
# Copyright (c) 2020 lilydjwg <lilydjwg@gmail.com>, et al. # Copyright (c) 2020 lilydjwg <lilydjwg@gmail.com>, et al.
# Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al. # Copyright (c) 2017 Felix Yan <felixonmars@archlinux.org>, et al.
from nvchecker.api import GetVersionError from nvchecker.api import RichResult, GetVersionError
URL = 'https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=%s&exact_match=true' URL = 'https://api.launchpad.net/1.0/ubuntu/+archive/primary?ws.op=getPublishedSources&source_name=%s&exact_match=true'
@ -42,4 +42,7 @@ async def get_version(name, conf, *, cache, **kwargs):
else: else:
version = releases[0]['source_package_version'] version = releases[0]['source_package_version']
return version return RichResult(
version = version,
url = f'https://packages.ubuntu.com/{releases[0]["distro_series_link"].rsplit("/", 1)[-1]}/{pkg}',
)

View file

@ -3,7 +3,7 @@
from nvchecker.api import ( from nvchecker.api import (
VersionResult, Entry, AsyncCache, KeyManager, VersionResult, Entry, AsyncCache, KeyManager,
TemporaryError, session, GetVersionError, TemporaryError, session, RichResult, GetVersionError,
) )
API_URL = 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery' API_URL = 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery'
@ -51,4 +51,7 @@ async def get_version(name: str, conf: Entry, *, cache: AsyncCache, **kwargs):
j = res.json() j = res.json()
version = j['results'][0]['extensions'][0]['versions'][0]['version'] version = j['results'][0]['extensions'][0]['versions'][0]['version']
return version return RichResult(
version = version,
url = f'https://marketplace.visualstudio.com/items?itemName={name}',
)