mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-09 04:38:55 -05:00
[Core, Downloader] Clear lib folder on minor Python version change, add [p]cog reinstallreqs command (#3274)
* feat(downloader): add `[p]cog reinstallreqs` command * enhance: clear lib folder on minor Python version change * chore(changelog): add towncrier entries * enhance: warn user about detected change in OS or arch * enhance: use actual prefix instead of `[p]` * Whoops... Co-Authored-By: Michael H <michael@michaelhall.tech> * enhance: wrap message sending in try except Co-authored-by: Michael H <michael@michaelhall.tech>
This commit is contained in:
parent
b0f840c273
commit
474bb0904e
1
changelog.d/3274.enhance.1.rst
Normal file
1
changelog.d/3274.enhance.1.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
Lib folder is now cleared on minor Python version change. `[p]cog reinstallreqs` command in Downloader cog can be used to regenerate lib folder for new Python version.
|
||||||
1
changelog.d/3274.enhance.2.rst
Normal file
1
changelog.d/3274.enhance.2.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
If Red detects operating system or architecture change, it will warn owner about possible problem with lib folder.
|
||||||
1
changelog.d/downloader/3167.feature.rst
Normal file
1
changelog.d/downloader/3167.feature.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
Added `[p]cog reinstallreqs` command that allows to reinstall cog requirements and shared libraries for all installed cogs.
|
||||||
@ -50,13 +50,18 @@ class Downloader(commands.Cog):
|
|||||||
self.SHAREDLIB_PATH = self.LIB_PATH / "cog_shared"
|
self.SHAREDLIB_PATH = self.LIB_PATH / "cog_shared"
|
||||||
self.SHAREDLIB_INIT = self.SHAREDLIB_PATH / "__init__.py"
|
self.SHAREDLIB_INIT = self.SHAREDLIB_PATH / "__init__.py"
|
||||||
|
|
||||||
|
self._create_lib_folder()
|
||||||
|
|
||||||
|
self._repo_manager = RepoManager()
|
||||||
|
|
||||||
|
def _create_lib_folder(self, *, remove_first: bool = False) -> None:
|
||||||
|
if remove_first:
|
||||||
|
shutil.rmtree(str(self.LIB_PATH))
|
||||||
self.SHAREDLIB_PATH.mkdir(parents=True, exist_ok=True)
|
self.SHAREDLIB_PATH.mkdir(parents=True, exist_ok=True)
|
||||||
if not self.SHAREDLIB_INIT.exists():
|
if not self.SHAREDLIB_INIT.exists():
|
||||||
with self.SHAREDLIB_INIT.open(mode="w", encoding="utf-8") as _:
|
with self.SHAREDLIB_INIT.open(mode="w", encoding="utf-8") as _:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self._repo_manager = RepoManager()
|
|
||||||
|
|
||||||
async def initialize(self) -> None:
|
async def initialize(self) -> None:
|
||||||
await self._repo_manager.initialize()
|
await self._repo_manager.initialize()
|
||||||
await self._maybe_update_config()
|
await self._maybe_update_config()
|
||||||
@ -553,6 +558,59 @@ class Downloader(commands.Cog):
|
|||||||
"""Cog installation management commands."""
|
"""Cog installation management commands."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@cog.command(name="reinstallreqs")
|
||||||
|
async def _cog_reinstallreqs(self, ctx: commands.Context) -> None:
|
||||||
|
"""
|
||||||
|
This command will reinstall cog requirements and shared libraries for all installed cogs.
|
||||||
|
|
||||||
|
Red might ask user to use this when it clears contents of lib folder
|
||||||
|
because of change in minor version of Python.
|
||||||
|
"""
|
||||||
|
async with ctx.typing():
|
||||||
|
self._create_lib_folder(remove_first=True)
|
||||||
|
installed_cogs = await self.installed_cogs()
|
||||||
|
cogs = []
|
||||||
|
repos = set()
|
||||||
|
for cog in installed_cogs:
|
||||||
|
if cog.repo is None:
|
||||||
|
continue
|
||||||
|
repos.add(cog.repo)
|
||||||
|
cogs.append(cog)
|
||||||
|
failed_reqs = await self._install_requirements(cogs)
|
||||||
|
all_installed_libs: List[InstalledModule] = []
|
||||||
|
all_failed_libs: List[Installable] = []
|
||||||
|
for repo in repos:
|
||||||
|
installed_libs, failed_libs = await repo.install_libraries(
|
||||||
|
target_dir=self.SHAREDLIB_PATH, req_target_dir=self.LIB_PATH
|
||||||
|
)
|
||||||
|
all_installed_libs += installed_libs
|
||||||
|
all_failed_libs += failed_libs
|
||||||
|
message = ""
|
||||||
|
if failed_reqs:
|
||||||
|
message += _("Failed to install requirements: ") + humanize_list(
|
||||||
|
tuple(map(inline, failed_reqs))
|
||||||
|
)
|
||||||
|
if all_failed_libs:
|
||||||
|
libnames = [lib.name for lib in failed_libs]
|
||||||
|
message += _("\nFailed to install shared libraries: ") + humanize_list(
|
||||||
|
tuple(map(inline, libnames))
|
||||||
|
)
|
||||||
|
if message:
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"Cog requirements and shared libraries for all installed cogs"
|
||||||
|
" have been reinstalled but there were some errors:\n"
|
||||||
|
)
|
||||||
|
+ message
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"Cog requirements and shared libraries"
|
||||||
|
" for all installed cogs have been reinstalled."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@cog.command(name="install", usage="<repo_name> <cogs>")
|
@cog.command(name="install", usage="<repo_name> <cogs>")
|
||||||
async def _cog_install(self, ctx: commands.Context, repo: Repo, *cog_names: str) -> None:
|
async def _cog_install(self, ctx: commands.Context, repo: Repo, *cog_names: str) -> None:
|
||||||
"""Install a cog from the given repo."""
|
"""Install a cog from the given repo."""
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import asyncio
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -17,6 +19,7 @@ from discord.ext.commands import when_mentioned_or
|
|||||||
from . import Config, i18n, commands, errors, drivers, modlog, bank
|
from . import Config, i18n, commands, errors, drivers, modlog, bank
|
||||||
from .cog_manager import CogManager, CogManagerUI
|
from .cog_manager import CogManager, CogManagerUI
|
||||||
from .core_commands import license_info_command, Core
|
from .core_commands import license_info_command, Core
|
||||||
|
from .data_manager import cog_data_path
|
||||||
from .dev_commands import Dev
|
from .dev_commands import Dev
|
||||||
from .events import init_events
|
from .events import init_events
|
||||||
from .global_checks import init_global_checks
|
from .global_checks import init_global_checks
|
||||||
@ -79,6 +82,9 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
|
|||||||
disabled_command_msg="That command is disabled.",
|
disabled_command_msg="That command is disabled.",
|
||||||
extra_owner_destinations=[],
|
extra_owner_destinations=[],
|
||||||
owner_opt_out_list=[],
|
owner_opt_out_list=[],
|
||||||
|
last_system_info__python_version=[3, 7],
|
||||||
|
last_system_info__machine=None,
|
||||||
|
last_system_info__system=None,
|
||||||
schema_version=0,
|
schema_version=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -413,12 +419,71 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
|
|||||||
|
|
||||||
packages = []
|
packages = []
|
||||||
|
|
||||||
|
last_system_info = await self._config.last_system_info()
|
||||||
|
|
||||||
|
async def notify_owners(content: str) -> None:
|
||||||
|
destinations = await self.get_owner_notification_destinations()
|
||||||
|
for destination in destinations:
|
||||||
|
prefixes = await self.get_valid_prefixes(getattr(destination, "guild", None))
|
||||||
|
prefix = prefixes[0]
|
||||||
|
try:
|
||||||
|
await destination.send(content.format(prefix=prefix))
|
||||||
|
except Exception as _exc:
|
||||||
|
log.exception(
|
||||||
|
f"I could not send an owner notification to ({destination.id}){destination}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ver_info = list(sys.version_info[:2])
|
||||||
|
python_version_changed = False
|
||||||
|
LIB_PATH = cog_data_path(raw_name="Downloader") / "lib"
|
||||||
|
if ver_info != last_system_info["python_version"]:
|
||||||
|
await self._config.last_system_info.python_version.set(ver_info)
|
||||||
|
if any(LIB_PATH.iterdir()):
|
||||||
|
shutil.rmtree(str(LIB_PATH))
|
||||||
|
LIB_PATH.mkdir()
|
||||||
|
self.loop.create_task(
|
||||||
|
notify_owners(
|
||||||
|
"We detected a change in minor Python version"
|
||||||
|
" and cleared packages in lib folder.\n"
|
||||||
|
"The instance was started with no cogs, please load Downloader"
|
||||||
|
" and use `{prefix}cog reinstallreqs` to regenerate lib folder."
|
||||||
|
" After that, restart the bot to get"
|
||||||
|
" all of your previously loaded cogs loaded again."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
python_version_changed = True
|
||||||
|
else:
|
||||||
if cli_flags.no_cogs is False:
|
if cli_flags.no_cogs is False:
|
||||||
packages.extend(await self._config.packages())
|
packages.extend(await self._config.packages())
|
||||||
|
|
||||||
if cli_flags.load_cogs:
|
if cli_flags.load_cogs:
|
||||||
packages.extend(cli_flags.load_cogs)
|
packages.extend(cli_flags.load_cogs)
|
||||||
|
|
||||||
|
system_changed = False
|
||||||
|
machine = platform.machine()
|
||||||
|
system = platform.system()
|
||||||
|
if last_system_info["machine"] is None:
|
||||||
|
await self._config.last_system_info.machine.set(machine)
|
||||||
|
elif last_system_info["machine"] != machine:
|
||||||
|
await self._config.last_system_info.machine.set(machine)
|
||||||
|
system_changed = True
|
||||||
|
|
||||||
|
if last_system_info["system"] is None:
|
||||||
|
await self._config.last_system_info.system.set(system)
|
||||||
|
elif last_system_info["system"] != system:
|
||||||
|
await self._config.last_system_info.system.set(system)
|
||||||
|
system_changed = True
|
||||||
|
|
||||||
|
if system_changed and not python_version_changed:
|
||||||
|
self.loop.create_task(
|
||||||
|
notify_owners(
|
||||||
|
"We detected a possible change in machine's operating system"
|
||||||
|
" or architecture. You might need to regenerate your lib folder"
|
||||||
|
" if 3rd-party cogs stop working properly.\n"
|
||||||
|
"To regenerate lib folder, load Downloader and use `{prefix}cog reinstallreqs`."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if packages:
|
if packages:
|
||||||
# Load permissions first, for security reasons
|
# Load permissions first, for security reasons
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user