diff --git a/docs/changelog_3_1_0.rst b/docs/changelog_3_1_0.rst index 1d48cb9cf..773260f23 100644 --- a/docs/changelog_3_1_0.rst +++ b/docs/changelog_3_1_0.rst @@ -60,6 +60,8 @@ Downloader * Fixed bug, that caused Downloader to include submodules on cog list (`#2590`_) * ``[p]cog uninstall`` allows to uninstall multiple cogs now (`#2592`_) * ``[p]cog uninstall`` will now remove cog from installed cogs even if it can't find the cog in install path anymore (`#2595`_) + * ``[p]cog install`` will not allow to install cogs which aren't suitable for installed version of Red anymore (`#2605`_) + * Cog Developers now have to use ``min_bot_version`` in form of version string instead of ``bot_version`` in info.json and they can also use ``max_bot_version`` to specify maximum version of Red, more in :doc:`framework_downloader`. (`#2605`_) --- Mod @@ -141,5 +143,6 @@ Utility Functions .. _#2600: https://github.com/Cog-Creators/Red-DiscordBot/pull/2600 .. _#2602: https://github.com/Cog-Creators/Red-DiscordBot/pull/2602 .. _#2604: https://github.com/Cog-Creators/Red-DiscordBot/pull/2604 +.. _#2605: https://github.com/Cog-Creators/Red-DiscordBot/pull/2605 .. _#2606: https://github.com/Cog-Creators/Red-DiscordBot/pull/2606 .. _#2620: https://github.com/Cog-Creators/Red-DiscordBot/pull/2620 diff --git a/docs/framework_downloader.rst b/docs/framework_downloader.rst index 45d75e459..6d4a5a45b 100644 --- a/docs/framework_downloader.rst +++ b/docs/framework_downloader.rst @@ -30,7 +30,10 @@ Keys common to both repo and cog info.json (case sensitive) Keys specific to the cog info.json (case sensitive) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- ``bot_version`` (list of integer) - Min version number of Red in the format ``(MAJOR, MINOR, PATCH)`` +- ``min_bot_version`` (string) - Min version number of Red in the format ``MAJOR.MINOR.MICRO`` + +- ``max_bot_version`` (string) - Max version number of Red in the format ``MAJOR.MINOR.MICRO``, + if ``min_bot_version`` is newer than ``max_bot_version``, ``max_bot_version`` will be ignored - ``hidden`` (bool) - Determines if a cog is visible in the cog list for a repo. diff --git a/redbot/__init__.py b/redbot/__init__.py index fb4b6c087..d2cd6b687 100644 --- a/redbot/__init__.py +++ b/redbot/__init__.py @@ -119,8 +119,14 @@ class VersionInfo: "dev_release": self.dev_release, } - def __lt__(self, other: "VersionInfo") -> bool: - tups: _List[_Tuple[int, int, int, int, int, int, int]] = [] + def _generate_comparison_tuples( + self, other: "VersionInfo" + ) -> _List[ + _Tuple[int, int, int, int, _Union[int, float], _Union[int, float], _Union[int, float]] + ]: + tups: _List[ + _Tuple[int, int, int, int, _Union[int, float], _Union[int, float], _Union[int, float]] + ] = [] for obj in (self, other): tups.append( ( @@ -133,8 +139,20 @@ class VersionInfo: obj.dev_release if obj.dev_release is not None else _inf, ) ) + return tups + + def __lt__(self, other: "VersionInfo") -> bool: + tups = self._generate_comparison_tuples(other) return tups[0] < tups[1] + def __eq__(self, other: "VersionInfo") -> bool: + tups = self._generate_comparison_tuples(other) + return tups[0] == tups[1] + + def __le__(self, other: "VersionInfo") -> bool: + tups = self._generate_comparison_tuples(other) + return tups[0] <= tups[1] + def __str__(self) -> str: ret = f"{self.major}.{self.minor}.{self.micro}" if self.releaselevel != self.FINAL: diff --git a/redbot/cogs/downloader/downloader.py b/redbot/cogs/downloader/downloader.py index 8df78cbb9..d077478de 100644 --- a/redbot/cogs/downloader/downloader.py +++ b/redbot/cogs/downloader/downloader.py @@ -8,7 +8,7 @@ from sys import path as syspath from typing import Tuple, Union, Iterable import discord -from redbot.core import checks, commands, Config +from redbot.core import checks, commands, Config, version_info as red_version_info from redbot.core.bot import Red from redbot.core.data_manager import cog_data_path from redbot.core.i18n import Translator, cog_i18n @@ -303,6 +303,26 @@ class Downloader(commands.Cog): ) ) return + ignore_max = cog.min_bot_version > cog.max_bot_version + if ( + cog.min_bot_version > red_version_info + or not ignore_max + and cog.max_bot_version < red_version_info + ): + await ctx.send( + _("This cog requires at least Red version {min_version}").format( + min_version=cog.min_bot_version + ) + + ( + "" + if ignore_max + else _(" and at most {max_version}").format(max_version=cog.max_bot_version) + ) + + _(", but you have {current_version}, aborting install.").format( + current_version=red_version_info + ) + ) + return if not await repo.install_requirements(cog, self.LIB_PATH): libraries = humanize_list(tuple(map(inline, cog.requirements))) diff --git a/redbot/cogs/downloader/installable.py b/redbot/cogs/downloader/installable.py index a06e069ca..143701a6a 100644 --- a/redbot/cogs/downloader/installable.py +++ b/redbot/cogs/downloader/installable.py @@ -8,6 +8,8 @@ from typing import MutableMapping, Any, TYPE_CHECKING from .log import log from .json_mixins import RepoJSONMixin +from redbot.core import __version__, version_info as red_version_info, VersionInfo + if TYPE_CHECKING: from .repo_manager import RepoManager @@ -72,7 +74,8 @@ class Installable(RepoJSONMixin): self.repo_name = self._location.parent.stem self.author = () - self.bot_version = (3, 0, 0) + self.min_bot_version = red_version_info + self.max_bot_version = red_version_info self.min_python_version = (3, 5, 1) self.hidden = False self.disabled = False @@ -157,10 +160,16 @@ class Installable(RepoJSONMixin): self.author = author try: - bot_version = tuple(info.get("bot_version", [3, 0, 0])) + min_bot_version = VersionInfo.from_str(str(info.get("min_bot_version", __version__))) except ValueError: - bot_version = self.bot_version - self.bot_version = bot_version + min_bot_version = self.min_bot_version + self.min_bot_version = min_bot_version + + try: + max_bot_version = VersionInfo.from_str(str(info.get("max_bot_version", __version__))) + except ValueError: + max_bot_version = self.max_bot_version + self.max_bot_version = max_bot_version try: min_python_version = tuple(info.get("min_python_version", [3, 5, 1])) diff --git a/redbot/pytest/downloader.py b/redbot/pytest/downloader.py index 591441bf3..4c6e7d342 100644 --- a/redbot/pytest/downloader.py +++ b/redbot/pytest/downloader.py @@ -83,7 +83,8 @@ def bot_repo(event_loop): # Installable INFO_JSON = { "author": ("tekulvw",), - "bot_version": (3, 0, 0), + "min_bot_version": "3.0.0", + "max_bot_version": "3.0.2", "description": "A long description", "hidden": False, "install_msg": "A post-installation message", @@ -96,7 +97,8 @@ INFO_JSON = { LIBRARY_INFO_JSON = { "author": ("seputaes",), - "bot_version": (3, 0, 0), + "min_bot_version": "3.0.0", + "max_bot_version": "3.0.2", "description": "A long library description", "hidden": False, # libraries are always hidden, this tests it will be flipped "install_msg": "A library install message", diff --git a/tests/cogs/downloader/test_installable.py b/tests/cogs/downloader/test_installable.py index 5f1489e86..730ae9bcb 100644 --- a/tests/cogs/downloader/test_installable.py +++ b/tests/cogs/downloader/test_installable.py @@ -5,12 +5,15 @@ import pytest from redbot.pytest.downloader import * from redbot.cogs.downloader.installable import Installable, InstallableType +from redbot.core import VersionInfo def test_process_info_file(installable): for k, v in INFO_JSON.items(): if k == "type": assert installable.type == InstallableType.COG + elif k in ("min_bot_version", "max_bot_version"): + assert getattr(installable, k) == VersionInfo.from_str(v) else: assert getattr(installable, k) == v @@ -19,6 +22,8 @@ def test_process_lib_info_file(library_installable): for k, v in LIBRARY_INFO_JSON.items(): if k == "type": assert library_installable.type == InstallableType.SHARED_LIBRARY + elif k in ("min_bot_version", "max_bot_version"): + assert getattr(library_installable, k) == VersionInfo.from_str(v) elif k == "hidden": # libraries are always hidden, even if False assert library_installable.hidden is True