Add redbot-update command for updating Red (#6734)

This commit is contained in:
Jakub Kuczys
2026-05-14 00:14:43 +02:00
committed by GitHub
parent 899f24ceca
commit 7e2a74b276
22 changed files with 3016 additions and 76 deletions
+10 -60
View File
@@ -1,6 +1,7 @@
import asyncio
import contextlib
import platform
import shlex
import sys
import logging
import traceback
@@ -9,8 +10,7 @@ from typing import Tuple
import aiohttp
import discord
import importlib.metadata
from packaging.requirements import Requirement
import redbot_update
from packaging.specifiers import SpecifierSet
from packaging.version import Version
from redbot.core import data_manager
@@ -53,7 +53,7 @@ ______ _ ______ _ _ ______ _
_ = Translator(__name__, __file__)
def get_outdated_red_messages(pypi_version: str, requires_python: SpecifierSet) -> Tuple[str, str]:
def get_outdated_red_messages(pypi_version: str) -> Tuple[str, str]:
outdated_red_message = _(
"Your Red instance is out of date! {} is the current version, however you are using {}!"
).format(pypi_version, red_version)
@@ -62,7 +62,6 @@ def get_outdated_red_messages(pypi_version: str, requires_python: SpecifierSet)
f"[red]!!![/red]Version [cyan]{pypi_version}[/] is available, "
f"but you're using [cyan]{red_version}[/][red]!!![/red]"
)
current_python = Version(platform.python_version())
extra_update = _(
"\n\nWhile the following command should work in most scenarios as it is "
"based on your current OS, environment, and Python version, "
@@ -71,64 +70,15 @@ def get_outdated_red_messages(pypi_version: str, requires_python: SpecifierSet)
"needs to be done during the update.**"
).format(docs="https://docs.discord.red/en/stable/update_red.html")
if current_python not in requires_python:
extra_update += _(
"\n\nYou have Python `{py_version}` and this update "
"requires `{req_py}`; you cannot simply run the update command.\n\n"
"You will need to follow the update instructions in our docs above, "
"if you still need help updating after following the docs go to our "
"#support channel in <https://discord.gg/red>"
).format(py_version=current_python, req_py=requires_python)
outdated_red_message += extra_update
return outdated_red_message, rich_outdated_message
red_dist = importlib.metadata.distribution("Red-DiscordBot")
installed_extras = red_dist.metadata.get_all("Provides-Extra")
installed_extras.remove("dev")
installed_extras.remove("all")
distributions = {}
for req_str in red_dist.requires:
req = Requirement(req_str)
if req.marker is None or req.marker.evaluate():
continue
for extra in reversed(installed_extras):
if not req.marker.evaluate({"extra": extra}):
continue
# Check that the requirement is met.
# This is a bit simplified for our purposes and does not check
# whether the requirements of our requirements are met as well.
# This could potentially be an issue if we'll ever depend on
# a dependency's extra in our extra when we already depend on that
# in our base dependencies. However, considering that right now, all
# our dependencies are also fully pinned, this should not ever matter.
if req.name in distributions:
dist = distributions[req.name]
else:
try:
dist = importlib.metadata.distribution(req.name)
except importlib.metadata.PackageNotFoundError:
dist = None
distributions[req.name] = dist
if dist is None or not req.specifier.contains(dist.version, prereleases=True):
installed_extras.remove(extra)
if installed_extras:
package_extras = f"[{','.join(installed_extras)}]"
else:
package_extras = ""
redbot_update_bin = redbot_update.find_redbot_update_bin()
is_windows = platform.system() == "Windows"
update_command = f'"{redbot_update_bin}"' if is_windows else shlex.quote(redbot_update_bin)
extra_update += _(
"\n\nTo update your bot, first shutdown your bot"
" then open a window of {console} (Not as admin) and run the following:"
"{command_1}\n"
"Once you've started up your bot again, we recommend that"
" you update any installed 3rd-party cogs with this command in Discord:"
"{command_2}"
" then open a window of {console} (Not as admin) and run the following: {command}"
).format(
console=_("Command Prompt") if platform.system() == "Windows" else _("Terminal"),
command_1=f'```"{sys.executable}" -m pip install -U "Red-DiscordBot{package_extras}"```',
command_2=f"```[p]cog update```",
console=_("Command Prompt") if is_windows else _("Terminal"),
command=f"```{update_command}```",
)
outdated_red_message += extra_update
return outdated_red_message, rich_outdated_message
@@ -224,7 +174,7 @@ def init_events(bot, cli_flags):
outdated = latest.version > Version(red_version)
if outdated:
outdated_red_message, rich_outdated_message = get_outdated_red_messages(
latest.version, latest.requires_python
latest.version
)
rich_console.print(rich_outdated_message)
await send_to_owners_with_prefix_replaced(bot, outdated_red_message)