From d5cc27600075562a689981971937c11a20d3ec4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ila=C3=AF=20Deutel?= Date: Fri, 27 Sep 2024 00:10:21 -0400 Subject: [PATCH] crates.io: skip pre-releases, list option This adds a `use_pre_release` option and adds support for list option for crates.io, similar to PyPI. --- docs/usage.rst | 5 +++++ nvchecker_source/cratesio.py | 35 ++++++++++++++++++++++++++++++----- tests/test_cratesio.py | 20 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 642c82d5..98b65254 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -661,6 +661,11 @@ Check `crates.io `_ for updates. cratesio The crate name on crates.io, e.g. ``tokio``. +use_pre_release + Whether to accept pre release. Default is false. + +This source supports :ref:`list options`. + Check Local Pacman Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/nvchecker_source/cratesio.py b/nvchecker_source/cratesio.py index 7c1f0d17..6aa0aeb0 100644 --- a/nvchecker_source/cratesio.py +++ b/nvchecker_source/cratesio.py @@ -1,15 +1,40 @@ # MIT licensed # Copyright (c) 2013-2020 lilydjwg , et al. +import re + +import structlog + from nvchecker.api import RichResult +logger = structlog.get_logger(logger_name=__name__) + + API_URL = 'https://crates.io/api/v1/crates/%s' +# https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string +VERSION_PATTERN = r'^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$' + async def get_version(name, conf, *, cache, **kwargs): name = conf.get('cratesio') or name + use_pre_release = conf.get('use_pre_release', False) data = await cache.get_json(API_URL % name) - version = [v['num'] for v in data['versions'] if not v['yanked']][0] - return RichResult( - version = version, - url = f'https://crates.io/crates/{name}/{version}', - ) + results = [] + for v in data['versions']: + if v['yanked']: + continue + version = v['num'] + match = re.fullmatch(VERSION_PATTERN, version) + if match is None: + logger.warning('ignoring invalid version', version=version) + continue + if not use_pre_release and match.group('prerelease'): + continue + results.append( + RichResult( + version=version, + url=f'https://crates.io/crates/{name}/{version}', + ) + ) + + return results diff --git a/tests/test_cratesio.py b/tests/test_cratesio.py index bb4bf25f..ee397c5b 100644 --- a/tests/test_cratesio.py +++ b/tests/test_cratesio.py @@ -8,3 +8,23 @@ async def test_cratesio(get_version): assert await get_version("example", { "source": "cratesio", }) == "1.1.0" + +async def test_cratesio_list(get_version): + assert await get_version("example", { + "source": "cratesio", + "include_regex": r"^1\.0.*", + }) == "1.0.2" + +async def test_cratesio_skip_prerelease(get_version): + with pytest.raises(RuntimeError, match='include_regex matched no versions'): + await get_version("cargo-lock", { + "source": "cratesio", + "include_regex": r".*-.*", + }) + +async def test_cratesio_use_prerelease(get_version): + await get_version("cargo-lock", { + "source": "cratesio", + "use_pre_release": "true", + "include_regex": r".*-.*", + })