From a04d6b0fc6372015034836b3581bf2eb6a1b8dea Mon Sep 17 00:00:00 2001 From: Daniel Peukert Date: Wed, 18 Oct 2023 01:59:30 +0200 Subject: [PATCH 1/5] implement rich result support for list-based sources --- nvchecker/core.py | 16 ++++++++++------ nvchecker/util.py | 10 ++++++++-- nvchecker_source/bitbucket.py | 9 ++++----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/nvchecker/core.py b/nvchecker/core.py index ea97e81..89c62f4 100644 --- a/nvchecker/core.py +++ b/nvchecker/core.py @@ -298,30 +298,31 @@ def substitute_version( return version def apply_list_options( - versions: List[str], conf: Entry, -) -> Optional[str]: + versions: List[Union[str, RichResult]], conf: Entry, +) -> Optional[Union[str, RichResult]]: pattern = conf.get('include_regex') if pattern: re_pat = re.compile(pattern) versions = [x for x in versions - if re_pat.fullmatch(x)] + if re_pat.fullmatch(str(x))] pattern = conf.get('exclude_regex') if pattern: re_pat = re.compile(pattern) versions = [x for x in versions - if not re_pat.fullmatch(x)] + if not re_pat.fullmatch(str(x))] ignored = set(conf.get('ignored', '').split()) if ignored: - versions = [x for x in versions if x not in ignored] + versions = [x for x in versions + if str(x) not in ignored] if not versions: return None sort_version_key = sort_version_keys[ conf.get("sort_version_key", "parse_version")] - versions.sort(key=sort_version_key) # type: ignore + versions.sort(key=lambda version: sort_version_key(str(version))) # type: ignore return versions[-1] @@ -342,6 +343,9 @@ def _process_result(r: RawResult) -> Union[Result, Exception]: return version elif isinstance(version, list): version_str = apply_list_options(version, conf) + if isinstance(version_str, RichResult): + url = version_str.url + version_str = version_str.version elif isinstance(version, RichResult): version_str = version.version url = version.url diff --git a/nvchecker/util.py b/nvchecker/util.py index b6463b3..e9f4709 100644 --- a/nvchecker/util.py +++ b/nvchecker/util.py @@ -45,19 +45,25 @@ if sys.version_info[:2] >= (3, 10): class RichResult: version: str url: Optional[str] = None + + def __str__(self): + return self.version else: @dataclass class RichResult: version: str url: Optional[str] = None -VersionResult = Union[None, str, List[str], RichResult, Exception] + def __str__(self): + return self.version + +VersionResult = Union[None, str, RichResult, List[Union[str, RichResult]], Exception] VersionResult.__doc__ = '''The result of a `get_version` check. * `None` - No version found. * `str` - A single version string is found. -* `List[str]` - Multiple version strings are found. :ref:`list options` will be applied. * `RichResult` - A version string with additional information. +* `List[Union[str, RichResult]]` - Multiple version strings with or without additional information are found. :ref:`list options` will be applied. * `Exception` - An error occurred. ''' diff --git a/nvchecker_source/bitbucket.py b/nvchecker_source/bitbucket.py index 6039454..211ff04 100644 --- a/nvchecker_source/bitbucket.py +++ b/nvchecker_source/bitbucket.py @@ -1,10 +1,10 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. -from typing import Any, List +from typing import Any, List, Union from urllib.parse import urlencode -from nvchecker.api import VersionResult, Entry, AsyncCache +from nvchecker.api import VersionResult, RichResult, Entry, AsyncCache # doc: https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-commits-get BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s' @@ -54,8 +54,8 @@ async def _get_tags( url: str, *, max_page: int, cache: AsyncCache, -) -> List[str]: - ret: List[str] = [] +) -> VersionResult: + ret: List[Union[str, RichResult]] = [] for _ in range(max_page): data = await cache.get_json(url) @@ -66,4 +66,3 @@ async def _get_tags( break return ret - From 5a6fee28176403337d11053640aabcaea512cebd Mon Sep 17 00:00:00 2001 From: Daniel Peukert Date: Wed, 18 Oct 2023 01:59:43 +0200 Subject: [PATCH 2/5] fix pacman test --- tests/test_pacman.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pacman.py b/tests/test_pacman.py index 006223f..377b177 100644 --- a/tests/test_pacman.py +++ b/tests/test_pacman.py @@ -13,7 +13,7 @@ pytestmark = [pytest.mark.asyncio, async def test_pacman(get_version): assert await get_version("base", { "source": "pacman", - }) == "3-1" + }) == "3-2" async def test_pacman_strip_release(get_version): assert await get_version("base", { From 6bf34873d3b7f663b1d85a610b6923ef8a9b8db4 Mon Sep 17 00:00:00 2001 From: Daniel Peukert Date: Wed, 18 Oct 2023 02:00:46 +0200 Subject: [PATCH 3/5] implement first batch of URL results for sources --- nvchecker_source/anitya.py | 7 ++++- nvchecker_source/apt.py | 38 ++++++++++++++++++++++----- nvchecker_source/archpkg.py | 7 +++-- nvchecker_source/cpan.py | 8 ++++-- nvchecker_source/cran.py | 7 +++-- nvchecker_source/cratesio.py | 7 ++++- nvchecker_source/debianpkg.py | 7 +++-- nvchecker_source/gems.py | 9 ++++++- nvchecker_source/hackage.py | 9 +++++-- nvchecker_source/npm.py | 13 ++++++++-- nvchecker_source/openvsx.py | 7 ++++- nvchecker_source/packagist.py | 8 +++++- nvchecker_source/pagure.py | 12 ++++++--- nvchecker_source/repology.py | 10 ++++--- nvchecker_source/sparkle.py | 43 ++++++++++++++++++++++++------- nvchecker_source/ubuntupkg.py | 7 +++-- nvchecker_source/vsmarketplace.py | 7 +++-- 17 files changed, 162 insertions(+), 44 deletions(-) diff --git a/nvchecker_source/anitya.py b/nvchecker_source/anitya.py index 6043749..db5d8ca 100644 --- a/nvchecker_source/anitya.py +++ b/nvchecker_source/anitya.py @@ -1,10 +1,15 @@ # MIT licensed # Copyright (c) 2017-2020 lilydjwg , et al. +from nvchecker.api import RichResult + URL = 'https://release-monitoring.org/api/project/{pkg}' async def get_version(name, conf, *, cache, **kwargs): pkg = conf.get('anitya') url = URL.format(pkg = pkg) data = await cache.get_json(url) - return data['version'] + return RichResult( + version = data['version'], + url = f'https://release-monitoring.org/project/{data["id"]}/', + ) diff --git a/nvchecker_source/apt.py b/nvchecker_source/apt.py index 8848c69..86ee39e 100644 --- a/nvchecker_source/apt.py +++ b/nvchecker_source/apt.py @@ -11,8 +11,8 @@ import functools from collections import defaultdict from nvchecker.api import ( - session, GetVersionError, - VersionResult, Entry, AsyncCache, KeyManager, + session, GetVersionError, VersionResult, + RichResult, Entry, AsyncCache, KeyManager, ) APT_RELEASE_URL = "%s/dists/%s/Release" @@ -92,12 +92,13 @@ async def get_url(url: str) -> str: None, _decompress_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 apt_packages = await cache.get(url, get_url) # type: ignore pkg_map = defaultdict(list) srcpkg_map = defaultdict(list) + pkg_to_src_map = defaultdict(list) pkg = None srcpkg = None @@ -110,6 +111,7 @@ async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], D version = line[9:] if pkg is not None: 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: srcpkg_map[srcpkg].append(version) 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()} srcpkg_map_max = {pkg: max(vs, key=functools.cmp_to_key(compare_version)) 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( name: str, conf: Entry, *, @@ -148,16 +152,38 @@ async def get_version( else: 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 if pkg and pkg in pkg_map: version = pkg_map[pkg] + changelog_name = pkg_to_src_map[pkg] elif srcpkg and srcpkg in srcpkg_map: version = srcpkg_map[srcpkg] + changelog_name = srcpkg else: 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: version = version.split("-")[0] - return version + + if changelog is not None: + return RichResult( + version = version, + url = changelog, + ) + else: + return version diff --git a/nvchecker_source/archpkg.py b/nvchecker_source/archpkg.py index de321c4..99bb834 100644 --- a/nvchecker_source/archpkg.py +++ b/nvchecker_source/archpkg.py @@ -1,7 +1,7 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. -from nvchecker.api import session, GetVersionError +from nvchecker.api import session, RichResult, GetVersionError URL = 'https://www.archlinux.org/packages/search/json/' @@ -31,4 +31,7 @@ async def get_version(name, conf, *, cache, **kwargs): else: version = r['pkgver'] + '-' + r['pkgrel'] - return version + return RichResult( + version = version, + url = f'https://archlinux.org/packages/{r["repo"]}/{r["arch"]}/{r["pkgname"]}/', + ) diff --git a/nvchecker_source/cpan.py b/nvchecker_source/cpan.py index 413a1f2..4da5a75 100644 --- a/nvchecker_source/cpan.py +++ b/nvchecker_source/cpan.py @@ -1,11 +1,15 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +from nvchecker.api import RichResult + # Using metacpan CPAN_URL = 'https://fastapi.metacpan.org/release/%s' async def get_version(name, conf, *, cache, **kwargs): key = conf.get('cpan', name) 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"]}', + ) diff --git a/nvchecker_source/cran.py b/nvchecker_source/cran.py index 53061ba..b648e7d 100644 --- a/nvchecker_source/cran.py +++ b/nvchecker_source/cran.py @@ -1,7 +1,7 @@ # MIT licensed # Copyright (c) 2022 Pekka Ristola , 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' VERSION_FIELD = 'Version: ' @@ -23,4 +23,7 @@ async def get_version(name, conf, *, cache, **kwargs): else: raise GetVersionError('Invalid DESCRIPTION file') - return version + return RichResult( + version = version, + url = f'https://cran.r-project.org/web/packages/{package}/', + ) diff --git a/nvchecker_source/cratesio.py b/nvchecker_source/cratesio.py index 4624c22..7c1f0d1 100644 --- a/nvchecker_source/cratesio.py +++ b/nvchecker_source/cratesio.py @@ -1,10 +1,15 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +from nvchecker.api import RichResult + API_URL = 'https://crates.io/api/v1/crates/%s' async def get_version(name, conf, *, cache, **kwargs): name = conf.get('cratesio') or name data = await cache.get_json(API_URL % name) 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}', + ) diff --git a/nvchecker_source/debianpkg.py b/nvchecker_source/debianpkg.py index 232151b..7ef2723 100644 --- a/nvchecker_source/debianpkg.py +++ b/nvchecker_source/debianpkg.py @@ -2,7 +2,7 @@ # Copyright (c) 2020 lilydjwg , et al. # Copyright (c) 2017 Felix Yan , 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' @@ -22,4 +22,7 @@ async def get_version(name, conf, *, cache, **kwargs): else: version = r['version'] - return version + return RichResult( + version = version, + url = f'https://sources.debian.org/src/{data["package"]}/{r["version"]}/', + ) diff --git a/nvchecker_source/gems.py b/nvchecker_source/gems.py index 259c635..4cae35e 100644 --- a/nvchecker_source/gems.py +++ b/nvchecker_source/gems.py @@ -1,9 +1,16 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +from nvchecker.api import RichResult + GEMS_URL = 'https://rubygems.org/api/v1/versions/%s.json' async def get_version(name, conf, *, cache, **kwargs): key = conf.get('gems', name) 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 + ] diff --git a/nvchecker_source/hackage.py b/nvchecker_source/hackage.py index 2f8b9f4..b96d254 100644 --- a/nvchecker_source/hackage.py +++ b/nvchecker_source/hackage.py @@ -1,10 +1,15 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +from nvchecker.api import RichResult + HACKAGE_URL = 'https://hackage.haskell.org/package/%s/preferred.json' async def get_version(name, conf, *, cache, **kwargs): key = conf.get('hackage', name) 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}', + ) diff --git a/nvchecker_source/npm.py b/nvchecker_source/npm.py index 19dda4a..46d2b41 100644 --- a/nvchecker_source/npm.py +++ b/nvchecker_source/npm.py @@ -3,7 +3,7 @@ import json import re -from nvchecker.api import session +from nvchecker.api import session, RichResult 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) 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 diff --git a/nvchecker_source/openvsx.py b/nvchecker_source/openvsx.py index 7b65119..44ba056 100644 --- a/nvchecker_source/openvsx.py +++ b/nvchecker_source/openvsx.py @@ -1,6 +1,8 @@ # MIT licensed # Copyright (c) 2013-2021 Th3Whit3Wolf , et al. +from nvchecker.api import RichResult + API_URL = 'https://open-vsx.org/api/%s/%s' async def get_version(name, conf, *, cache, **kwargs): @@ -10,4 +12,7 @@ async def get_version(name, conf, *, cache, **kwargs): extension = splitName[1] data = await cache.get_json(API_URL % (publisher, extension)) version = data['version'] - return version + return RichResult( + version = version, + url = f'https://open-vsx.org/extension/{publisher}/{extension}/{version}', + ) diff --git a/nvchecker_source/packagist.py b/nvchecker_source/packagist.py index c7f39cd..634275f 100644 --- a/nvchecker_source/packagist.py +++ b/nvchecker_source/packagist.py @@ -1,6 +1,8 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +from nvchecker.api import RichResult + PACKAGIST_URL = 'https://packagist.org/packages/%s.json' async def get_version(name, conf, *, cache, **kwargs): @@ -14,4 +16,8 @@ async def get_version(name, conf, *, cache, **kwargs): } 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}', + ) diff --git a/nvchecker_source/pagure.py b/nvchecker_source/pagure.py index 2dd2bdd..70c9974 100644 --- a/nvchecker_source/pagure.py +++ b/nvchecker_source/pagure.py @@ -6,10 +6,10 @@ import urllib.parse import structlog 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__) @@ -24,5 +24,9 @@ async def get_version( url = PAGURE_URL % (host, repo) data = await cache.get_json(url) - version = data["tags"] - return version + return [ + RichResult( + version = version, + url = f'https://{host}/{repo}/tree/{version_hash}', + ) for version, version_hash in data["tags"].items() + ] diff --git a/nvchecker_source/repology.py b/nvchecker_source/repology.py index 3e61a93..b08ca29 100644 --- a/nvchecker_source/repology.py +++ b/nvchecker_source/repology.py @@ -1,7 +1,7 @@ # MIT licensed # Copyright (c) 2019 lilydjwg , et al. -from nvchecker.api import GetVersionError +from nvchecker.api import RichResult, GetVersionError 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', repo=repo, subrepo=subrepo) - versions = [pkg['version'] for pkg in pkgs] - return versions + return [ + RichResult( + version = pkg['version'], + url = f'https://repology.org/project/{project}/packages', + ) for pkg in pkgs + ] diff --git a/nvchecker_source/sparkle.py b/nvchecker_source/sparkle.py index 1a754be..18e0ffc 100644 --- a/nvchecker_source/sparkle.py +++ b/nvchecker_source/sparkle.py @@ -4,23 +4,25 @@ from xml.etree import ElementTree -from nvchecker.api import session - -NAMESPACE = 'http://www.andymatuschak.org/xml-namespaces/sparkle' +from nvchecker.api import session, RichResult +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): 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) - root = ElementTree.fromstring(res.body) - item = root.find('./channel/item[1]/enclosure') + root = ElementTree.fromstring(res.body).find('./channel/item[1]') + item = root.find('./enclosure') - version_string = item.get(f'{{{NAMESPACE}}}shortVersionString') - build_number = item.get(f'{{{NAMESPACE}}}version') + version_string = item.get(f'{{{SPARKLE_NAMESPACE}}}shortVersionString') + build_number = item.get(f'{{{SPARKLE_NAMESPACE}}}version') if (version_string and version_string.isdigit()) and ( 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): 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 diff --git a/nvchecker_source/ubuntupkg.py b/nvchecker_source/ubuntupkg.py index 8525737..6553847 100644 --- a/nvchecker_source/ubuntupkg.py +++ b/nvchecker_source/ubuntupkg.py @@ -2,7 +2,7 @@ # Copyright (c) 2020 lilydjwg , et al. # Copyright (c) 2017 Felix Yan , 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' @@ -42,4 +42,7 @@ async def get_version(name, conf, *, cache, **kwargs): else: 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}', + ) diff --git a/nvchecker_source/vsmarketplace.py b/nvchecker_source/vsmarketplace.py index cb5035f..0b483d1 100644 --- a/nvchecker_source/vsmarketplace.py +++ b/nvchecker_source/vsmarketplace.py @@ -3,7 +3,7 @@ from nvchecker.api import ( VersionResult, Entry, AsyncCache, KeyManager, - TemporaryError, session, GetVersionError, + TemporaryError, session, RichResult, GetVersionError, ) 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() version = j['results'][0]['extensions'][0]['versions'][0]['version'] - return version + return RichResult( + version = version, + url = f'https://marketplace.visualstudio.com/items?itemName={name}', + ) From 592b4a4f6b5078b5429a79d79bdd056302897fe4 Mon Sep 17 00:00:00 2001 From: Daniel Peukert Date: Wed, 18 Oct 2023 02:03:10 +0200 Subject: [PATCH 4/5] update docs for sparkle --- docs/usage.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/usage.rst b/docs/usage.rst index e733049..9deac32 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -761,6 +761,9 @@ This enables you to track updates of macOS applications which using `Sparkle fra sparkle The url of the sparkle appcast. +release_notes_language + The language of release notes to return when localized release notes are available (defaults to ``en`` for English, the unlocalized release notes are used as a fallback) + Check Pagure ~~~~~~~~~~~~ :: From 55b3f671c876abf2646d0afec9659b153ac01519 Mon Sep 17 00:00:00 2001 From: Daniel Peukert Date: Wed, 18 Oct 2023 21:46:28 +0200 Subject: [PATCH 5/5] implement second batch of URL results for sources --- nvchecker_source/bitbucket.py | 21 +++++++++++------- nvchecker_source/gitea.py | 16 +++++++++---- nvchecker_source/github.py | 42 ++++++++++++++++++++++++----------- nvchecker_source/gitlab.py | 17 +++++++++----- 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/nvchecker_source/bitbucket.py b/nvchecker_source/bitbucket.py index 211ff04..183497d 100644 --- a/nvchecker_source/bitbucket.py +++ b/nvchecker_source/bitbucket.py @@ -22,7 +22,7 @@ async def get_version( use_sorted_tags = conf.get('use_sorted_tags', False) if use_sorted_tags or use_max_tag: - parameters = {'fields': 'values.name,next'} + parameters = {'fields': 'values.name,values.links.html.href,next'} if use_sorted_tags: parameters['sort'] = conf.get('sort', '-target.date') @@ -33,22 +33,22 @@ async def get_version( url = BITBUCKET_MAX_TAG % repo url += '?' + urlencode(parameters) - version = await _get_tags(url, max_page=1, cache=cache) + return await _get_tags(url, max_page=1, cache=cache) elif use_max_tag: url = BITBUCKET_MAX_TAG % repo url += '?' + urlencode(parameters) max_page = conf.get('max_page', 3) - version = await _get_tags(url, max_page=max_page, cache=cache) + return await _get_tags(url, max_page=max_page, cache=cache) else: url = BITBUCKET_URL % (repo, br) data = await cache.get_json(url) - - version = data['values'][0]['date'].split('T', 1)[0].replace('-', '') - - return version + return RichResult( + version = data['values'][0]['date'].split('T', 1)[0].replace('-', ''), + url = data['values'][0]['links']['html']['href'], + ) async def _get_tags( url: str, *, @@ -59,7 +59,12 @@ async def _get_tags( for _ in range(max_page): data = await cache.get_json(url) - ret.extend(x['name'] for x in data['values']) + ret.extend([ + RichResult( + version = tag['name'], + url = tag['links']['html']['href'], + ) for tag in data['values'] + ]) if 'next' in data: url = data['next'] else: diff --git a/nvchecker_source/gitea.py b/nvchecker_source/gitea.py index 50da908..0405554 100644 --- a/nvchecker_source/gitea.py +++ b/nvchecker_source/gitea.py @@ -9,7 +9,8 @@ GITEA_URL = 'https://%s/api/v1/repos/%s/commits' GITEA_MAX_TAG = 'https://%s/api/v1/repos/%s/tags' from nvchecker.api import ( - VersionResult, Entry, AsyncCache, KeyManager, + VersionResult, RichResult, Entry, + AsyncCache, KeyManager, ) async def get_version( @@ -42,7 +43,14 @@ async def get_version( data = await cache.get_json(url, headers = headers) if use_max_tag: - version = [tag["name"] for tag in data] + return [ + RichResult( + version = tag['name'], + url = f'https://{host}/{conf["gitea"]}/releases/tag/{tag["name"]}', + ) for tag in data + ] else: - version = data[0]['commit']['committer']['date'].split('T', 1)[0].replace('-', '') - return version + return RichResult( + version = data[0]['commit']['committer']['date'].split('T', 1)[0].replace('-', ''), + url = data[0]['html_url'], + ) diff --git a/nvchecker_source/github.py b/nvchecker_source/github.py index 0d512c2..e4f1b39 100644 --- a/nvchecker_source/github.py +++ b/nvchecker_source/github.py @@ -3,13 +3,13 @@ import time from urllib.parse import urlencode -from typing import Tuple +from typing import List, Tuple, Union import structlog from nvchecker.api import ( VersionResult, Entry, AsyncCache, KeyManager, - TemporaryError, session, GetVersionError, + TemporaryError, session, RichResult, GetVersionError, ) logger = structlog.get_logger(logger_name=__name__) @@ -49,6 +49,7 @@ QUERY_LATEST_RELEASE_WITH_PRERELEASES = ''' edges {{ node {{ name + url }} }} }} @@ -56,7 +57,7 @@ QUERY_LATEST_RELEASE_WITH_PRERELEASES = ''' }} ''' -async def get_latest_tag(key: Tuple[str, str, str]) -> str: +async def get_latest_tag(key: Tuple[str, str, str]) -> RichResult: repo, query, token = key owner, reponame = repo.split('/') headers = { @@ -80,9 +81,13 @@ async def get_latest_tag(key: Tuple[str, str, str]) -> str: if not refs: raise GetVersionError('no tag found') - return refs[0]['node']['name'] + version = refs[0]['node']['name'] + return RichResult( + version = version, + url = f'https://github.com/{repo}/releases/tag/{version}', + ) -async def get_latest_release_with_prereleases(key: Tuple[str, str]) -> str: +async def get_latest_release_with_prereleases(key: Tuple[str, str]) -> RichResult: repo, token = key owner, reponame = repo.split('/') headers = { @@ -105,7 +110,10 @@ async def get_latest_release_with_prereleases(key: Tuple[str, str]) -> str: if not refs: raise GetVersionError('no release found') - return refs[0]['node']['name'] + return RichResult( + version = refs[0]['node']['name'], + url = refs[0]['node']['url'], + ) async def get_version_real( name: str, conf: Entry, *, @@ -160,7 +168,12 @@ async def get_version_real( data = await cache.get_json(url, headers = headers) if use_max_tag: - tags = [ref['ref'].split('/', 2)[-1] for ref in data] + tags: List[Union[str, RichResult]] = [ + RichResult( + version = ref['ref'].split('/', 2)[-1], + url = f'https://github.com/{repo}/releases/tag/{ref["ref"].split("/", 2)[-1]}', + ) for ref in data + ] if not tags: raise GetVersionError('No tag found in upstream repository.') return tags @@ -168,14 +181,17 @@ async def get_version_real( if use_latest_release: if 'tag_name' not in data: raise GetVersionError('No release found in upstream repository.') - version = data['tag_name'] + return RichResult( + version = data['tag_name'], + url = data['html_url'], + ) else: - # YYYYMMDD.HHMMSS - version = data[0]['commit']['committer']['date'] \ - .rstrip('Z').replace('-', '').replace(':', '').replace('T', '.') - - return version + return RichResult( + # YYYYMMDD.HHMMSS + version = data[0]['commit']['committer']['date'].rstrip('Z').replace('-', '').replace(':', '').replace('T', '.'), + url = data[0]['html_url'], + ) def check_ratelimit(exc, name): res = exc.response diff --git a/nvchecker_source/gitlab.py b/nvchecker_source/gitlab.py index 844d93b..de4d4da 100644 --- a/nvchecker_source/gitlab.py +++ b/nvchecker_source/gitlab.py @@ -6,8 +6,8 @@ import urllib.parse import structlog from nvchecker.api import ( - VersionResult, Entry, AsyncCache, KeyManager, - TemporaryError, + VersionResult, RichResult, Entry, + AsyncCache, KeyManager, TemporaryError, ) GITLAB_URL = 'https://%s/api/v4/projects/%s/repository/commits' @@ -52,10 +52,17 @@ async def get_version_real( data = await cache.get_json(url, headers = headers) if use_max_tag: - version = [tag["name"] for tag in data] + return [ + RichResult( + version = tag['name'], + url = f'https://{host}/{conf["gitlab"]}/-/tags/{tag["name"]}', + ) for tag in data + ] else: - version = data[0]['created_at'].split('T', 1)[0].replace('-', '') - return version + return RichResult( + version = data[0]['created_at'].split('T', 1)[0].replace('-', ''), + url = data[0]['web_url'], + ) def check_ratelimit(exc, name): res = exc.response