From abc9927dea2efe382262966af8f07255fb6ff1ae Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:12:38 -0500 Subject: [PATCH 01/41] feat(backup): added the cog --- backup/__init__.py | 5 +++++ backup/backup.py | 43 +++++++++++++++++++++++++++++++++++++++++++ backup/info.json | 13 +++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 backup/__init__.py create mode 100644 backup/backup.py create mode 100644 backup/info.json diff --git a/backup/__init__.py b/backup/__init__.py new file mode 100644 index 0000000..2d8a158 --- /dev/null +++ b/backup/__init__.py @@ -0,0 +1,5 @@ +from .backup import Backup + + +async def setup(bot): + await bot.add_cog(Backup(bot)) diff --git a/backup/backup.py b/backup/backup.py new file mode 100644 index 0000000..e907d20 --- /dev/null +++ b/backup/backup.py @@ -0,0 +1,43 @@ +# _____ _ +# / ____| (_) +# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __ +# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__| +# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ | +# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| + +import json +import itertools + +from redbot.core import commands +from redbot.core.bot import Red + +class Backup(commands.Cog): + """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" + + __author__ = "SeaswimmerTheFsh" + __version__ = "1.0.0" + + def __init__(self, bot: Red): + super().__init__() + self.bot = bot + + @commands.group(autohelp=True) + @commands.is_owner() + async def backup(self, ctx: commands.Context): + """Backup your installed cogs.""" + + @backup.command(name='export') + @commands.is_owner() + async def backup_export(self, ctx: commands.Context): + """Export your installed repositories and cogs to a file.""" + downloader = ctx.bot.get_cog("Downloader") + if downloader is None: + await ctx.send(f"You do not have the `Downloader` cog loaded.\nPlease run `{ctx.prefix}load downloader` and try again.") + return + all_repos = list(downloader._repo_manager.repos) + repos_list = [[f"{i.name}", i.url] for i in all_repos] + cogs = await downloader.installed_cogs() + for cog, r in itertools.product(cogs, repos_list): + if cog.repo_name == list(r)[0]: + r.append(cog.name) + await ctx.send(json.dumps(repos_list, indent=4)) diff --git a/backup/info.json b/backup/info.json new file mode 100644 index 0000000..fa07072 --- /dev/null +++ b/backup/info.json @@ -0,0 +1,13 @@ +{ + "author" : ["SeaswimmerTheFsh"], + "install_msg" : "Thank you for installing Backup!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).", + "name" : "Backup", + "short" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.", + "description" : "A utility to make reinstalling repositories and cogs after migrating the bot far easier.", + "end_user_data_statement" : "This cog does not store user information.", + "hidden": false, + "disabled": false, + "min_bot_version": "3.5.0", + "min_python_version": [3, 10, 0], + "tags": [] +} From 7fa6cd5922742df8f4441581c887b0bc91fcdc96 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:18:55 -0500 Subject: [PATCH 02/41] feat(backup): changed export format --- backup/backup.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index e907d20..f5de2c7 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -10,6 +10,7 @@ import itertools from redbot.core import commands from redbot.core.bot import Red +from redbot.core.utils.chat_formatting import text_to_file class Backup(commands.Cog): """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" @@ -34,10 +35,24 @@ class Backup(commands.Cog): if downloader is None: await ctx.send(f"You do not have the `Downloader` cog loaded.\nPlease run `{ctx.prefix}load downloader` and try again.") return + all_repos = list(downloader._repo_manager.repos) - repos_list = [[f"{i.name}", i.url] for i in all_repos] - cogs = await downloader.installed_cogs() - for cog, r in itertools.product(cogs, repos_list): - if cog.repo_name == list(r)[0]: - r.append(cog.name) - await ctx.send(json.dumps(repos_list, indent=4)) + + export_data = [] + + for repo in all_repos: + repo_dict = { + "name": repo.name, + "url": repo.url, + "cogs": [] + } + + cogs = await downloader.installed_cogs() + + for cog in cogs: + if cog.repo_name == repo.name: + repo_dict["cogs"].append(cog.name) + + export_data.append(repo_dict) + + await ctx.send(file=text_to_file(json.dumps(export_data, indent=4), 'backup.json')) From 6fbfa96cf28eb213a4e7a00f47477ede04bb565a Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:31:37 -0500 Subject: [PATCH 03/41] feat(docs): added Backup documentation --- .docs/backup.md | 25 +++++++++++++++++++++++++ mkdocs.yml | 2 ++ 2 files changed, 27 insertions(+) create mode 100644 .docs/backup.md diff --git a/.docs/backup.md b/.docs/backup.md new file mode 100644 index 0000000..dfc499b --- /dev/null +++ b/.docs/backup.md @@ -0,0 +1,25 @@ +# Backup + +Backup allows you to export a JSON list of all of your installed repositories and cogs, then reimport them and automatically reinstall/reload the cogs. + +## Installation + +```bash +[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]cog install seacogs backup +[p]cog load backup +``` + +## Commands + +#### backup export + +- Usage: `[p]backup export` + +Exports a JSON list of all of your added repositories, and their installed cogs. + +#### backup import + +- Usage: `[p]backup import [json]` + +Imports, reinstalls, and reloads cogs from a valid export. Requires either the json export to be provided in the command's message content or to be attached to the command's message. Ignores itself and PyLav cogs. diff --git a/mkdocs.yml b/mkdocs.yml index 1f91b56..2b9fadd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,6 +16,7 @@ nav: - Moderation Commands: aurora/moderation-commands.md - Case Commands: aurora/case-commands.md - Configuration: aurora/configuration.md + - Backup: backup.md - Nerdify: nerdify.md plugins: @@ -100,4 +101,5 @@ theme: watch: - ./aurora + - ./backup - ./nerdify From 932c9f61edc11c7ec9bc5d78afcc1ee5b888283a Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:34:26 -0500 Subject: [PATCH 04/41] fix(docs): fixed incorrect headers --- .docs/backup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.docs/backup.md b/.docs/backup.md index dfc499b..2439d0d 100644 --- a/.docs/backup.md +++ b/.docs/backup.md @@ -12,13 +12,13 @@ Backup allows you to export a JSON list of all of your installed repositories an ## Commands -#### backup export +### backup export - Usage: `[p]backup export` Exports a JSON list of all of your added repositories, and their installed cogs. -#### backup import +### backup import - Usage: `[p]backup import [json]` From 35ffec9c3fa6e227228c1cff0b7b705eaad63a8c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:35:01 -0500 Subject: [PATCH 05/41] fix(backup): pylint fixes --- backup/backup.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index f5de2c7..f296eca 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -6,11 +6,10 @@ # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| import json -import itertools from redbot.core import commands from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import text_to_file +from redbot.core.utils.chat_formatting import error, text_to_file class Backup(commands.Cog): """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" @@ -33,10 +32,10 @@ class Backup(commands.Cog): """Export your installed repositories and cogs to a file.""" downloader = ctx.bot.get_cog("Downloader") if downloader is None: - await ctx.send(f"You do not have the `Downloader` cog loaded.\nPlease run `{ctx.prefix}load downloader` and try again.") + await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return - all_repos = list(downloader._repo_manager.repos) + all_repos = list(downloader._repo_manager.repos) # pylint: disable=protected-access export_data = [] @@ -56,3 +55,18 @@ class Backup(commands.Cog): export_data.append(repo_dict) await ctx.send(file=text_to_file(json.dumps(export_data, indent=4), 'backup.json')) + + @backup.command(name='import') + @commands.is_owner() + async def backup_import(self, ctx: commands.Context, json: str = None): + """Import your installed repositories and cogs from an export.""" + if json is None: + json = await ctx.message.attachments[0].read() + if json is None: + await ctx.send(error("Please provide a valid JSON export.")) + return + + downloader = ctx.bot.get_cog("Downloader") + if downloader is None: + await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) + return From 045d51bc237f02b1c0b3763e59f4e221350b24b1 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:37:27 -0500 Subject: [PATCH 06/41] fix(backup): only try to access message attachments if the message has any --- backup/backup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index f296eca..c2066a4 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -61,8 +61,9 @@ class Backup(commands.Cog): async def backup_import(self, ctx: commands.Context, json: str = None): """Import your installed repositories and cogs from an export.""" if json is None: - json = await ctx.message.attachments[0].read() - if json is None: + if ctx.message.attachments is not None: + json = await ctx.message.attachments[0].read() + else: await ctx.send(error("Please provide a valid JSON export.")) return From 26b085231b99740cb76842a8b7ee64f389e08cb5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:38:47 -0500 Subject: [PATCH 07/41] fix(backup): fixed list out of index error --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index c2066a4..4cbc618 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -61,7 +61,7 @@ class Backup(commands.Cog): async def backup_import(self, ctx: commands.Context, json: str = None): """Import your installed repositories and cogs from an export.""" if json is None: - if ctx.message.attachments is not None: + if not len(ctx.message.attachments) == 0: json = await ctx.message.attachments[0].read() else: await ctx.send(error("Please provide a valid JSON export.")) From d0375c3bdbc5541dd84154857a820f17a9cca2cc Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:55:35 -0500 Subject: [PATCH 08/41] fix(backup): loads the export to see if it's actually valid json or not --- backup/backup.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 4cbc618..39a4d09 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -58,14 +58,16 @@ class Backup(commands.Cog): @backup.command(name='import') @commands.is_owner() - async def backup_import(self, ctx: commands.Context, json: str = None): + async def backup_import(self, ctx: commands.Context, export: str = None): """Import your installed repositories and cogs from an export.""" - if json is None: + if export is None: if not len(ctx.message.attachments) == 0: - json = await ctx.message.attachments[0].read() - else: - await ctx.send(error("Please provide a valid JSON export.")) - return + export = await ctx.message.attachments[0].read() + try: + export = json.loads(json) + except json.JSONDecodeError: + await ctx.send(error("Please provide a valid JSON export.")) + return downloader = ctx.bot.get_cog("Downloader") if downloader is None: From 52f843938395cbf2fa954f880573c0d4f191ab8f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 12:56:51 -0500 Subject: [PATCH 09/41] fix(backup): lol. --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 39a4d09..c774b95 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -64,7 +64,7 @@ class Backup(commands.Cog): if not len(ctx.message.attachments) == 0: export = await ctx.message.attachments[0].read() try: - export = json.loads(json) + export = json.loads(export) except json.JSONDecodeError: await ctx.send(error("Please provide a valid JSON export.")) return From 702de214484614a41dd206716bfc7ee4ba78e0a6 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:03:02 -0500 Subject: [PATCH 10/41] fix(backup): added a kwarg to backup_import --- backup/backup.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index c774b95..bc2ef0b 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -58,11 +58,10 @@ class Backup(commands.Cog): @backup.command(name='import') @commands.is_owner() - async def backup_import(self, ctx: commands.Context, export: str = None): + async def backup_import(self, ctx: commands.Context, *, export: str = None): """Import your installed repositories and cogs from an export.""" - if export is None: - if not len(ctx.message.attachments) == 0: - export = await ctx.message.attachments[0].read() + if export is None and not len(ctx.message.attachments) == 0: + export = await ctx.message.attachments[0].read() try: export = json.loads(export) except json.JSONDecodeError: From 2dd8663e78f19926ed20a9b2c5f61e7e3f1a771c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:07:10 -0500 Subject: [PATCH 11/41] fix(backup): support codeblocks --- backup/backup.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index bc2ef0b..f499064 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -6,6 +6,7 @@ # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| import json +import re from redbot.core import commands from redbot.core.bot import Red @@ -63,7 +64,7 @@ class Backup(commands.Cog): if export is None and not len(ctx.message.attachments) == 0: export = await ctx.message.attachments[0].read() try: - export = json.loads(export) + export = json.loads(self.extract_json_from_codeblock(export)) except json.JSONDecodeError: await ctx.send(error("Please provide a valid JSON export.")) return @@ -72,3 +73,10 @@ class Backup(commands.Cog): if downloader is None: await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return + + def extract_json_from_codeblock(text): + match = re.match(r"```(?:\w+)?\n(.*?)\n```", text, re.DOTALL) + if match: + return match.group(1).strip() + else: + return text From 5eb762d9fe03ed4bc10f81429027081b7c216dec Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:07:48 -0500 Subject: [PATCH 12/41] fix(backup): fixed extract_json_from_codeblock --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index f499064..d70093d 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -74,7 +74,7 @@ class Backup(commands.Cog): await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return - def extract_json_from_codeblock(text): + def extract_json_from_codeblock(self, text): match = re.match(r"```(?:\w+)?\n(.*?)\n```", text, re.DOTALL) if match: return match.group(1).strip() From 168f62d43fec30c32579a55ee3170eb8b3e7b254 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:10:31 -0500 Subject: [PATCH 13/41] fix(backup): properly support codeblocks (maybe?) --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index d70093d..aa791ba 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -75,7 +75,7 @@ class Backup(commands.Cog): return def extract_json_from_codeblock(self, text): - match = re.match(r"```(?:\w+)?\n(.*?)\n```", text, re.DOTALL) + match = re.match(r"```(?:\w+)?\n(.*?)(? Date: Wed, 31 Jan 2024 13:11:17 -0500 Subject: [PATCH 14/41] Revert "fix(backup): properly support codeblocks (maybe?)" This reverts commit 168f62d43fec30c32579a55ee3170eb8b3e7b254. --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index aa791ba..d70093d 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -75,7 +75,7 @@ class Backup(commands.Cog): return def extract_json_from_codeblock(self, text): - match = re.match(r"```(?:\w+)?\n(.*?)(? Date: Wed, 31 Jan 2024 13:17:54 -0500 Subject: [PATCH 15/41] fix(backup): remove support for message content based json loading --- .docs/backup.md | 2 +- backup/backup.py | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.docs/backup.md b/.docs/backup.md index 2439d0d..787d1db 100644 --- a/.docs/backup.md +++ b/.docs/backup.md @@ -22,4 +22,4 @@ Exports a JSON list of all of your added repositories, and their installed cogs. - Usage: `[p]backup import [json]` -Imports, reinstalls, and reloads cogs from a valid export. Requires either the json export to be provided in the command's message content or to be attached to the command's message. Ignores itself and PyLav cogs. +Imports, reinstalls, and reloads cogs from a valid export. Requires the JSON export to be attached to the invoking message as an attachment. Ignores itself and PyLav cogs. diff --git a/backup/backup.py b/backup/backup.py index d70093d..0fcbccc 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -59,12 +59,12 @@ class Backup(commands.Cog): @backup.command(name='import') @commands.is_owner() - async def backup_import(self, ctx: commands.Context, *, export: str = None): - """Import your installed repositories and cogs from an export.""" + async def backup_import(self, ctx: commands.Context): + """Import your installed repositories and cogs from an export file.""" if export is None and not len(ctx.message.attachments) == 0: export = await ctx.message.attachments[0].read() try: - export = json.loads(self.extract_json_from_codeblock(export)) + export = json.loads(export) except json.JSONDecodeError: await ctx.send(error("Please provide a valid JSON export.")) return @@ -73,10 +73,3 @@ class Backup(commands.Cog): if downloader is None: await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return - - def extract_json_from_codeblock(self, text): - match = re.match(r"```(?:\w+)?\n(.*?)\n```", text, re.DOTALL) - if match: - return match.group(1).strip() - else: - return text From 037ef84dcc8e8cca608c7b3f8c9604d86ccdef26 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:19:26 -0500 Subject: [PATCH 16/41] fix(backup): added error message for if you don't provide an attachment --- backup/backup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 0fcbccc..e919a50 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -61,12 +61,15 @@ class Backup(commands.Cog): @commands.is_owner() async def backup_import(self, ctx: commands.Context): """Import your installed repositories and cogs from an export file.""" - if export is None and not len(ctx.message.attachments) == 0: + if not len(ctx.message.attachments) == 0: export = await ctx.message.attachments[0].read() + else: + await ctx.send(error("Please provide a valid JSON export file.")) + return try: export = json.loads(export) except json.JSONDecodeError: - await ctx.send(error("Please provide a valid JSON export.")) + await ctx.send(error("Please provide a valid JSON export file.")) return downloader = ctx.bot.get_cog("Downloader") From ac761d9284300cf0674b15c7f446f7455ff7ffed Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:21:24 -0500 Subject: [PATCH 17/41] misc(backup): condensed some code --- backup/backup.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index e919a50..70dd513 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -61,14 +61,10 @@ class Backup(commands.Cog): @commands.is_owner() async def backup_import(self, ctx: commands.Context): """Import your installed repositories and cogs from an export file.""" - if not len(ctx.message.attachments) == 0: - export = await ctx.message.attachments[0].read() - else: - await ctx.send(error("Please provide a valid JSON export file.")) - return try: - export = json.loads(export) - except json.JSONDecodeError: + export_raw = await ctx.message.attachments[0].read() + export = json.loads(export_raw) + except (json.JSONDecodeError, IndexError): await ctx.send(error("Please provide a valid JSON export file.")) return From 7fdb106fad831a218acd4cad3965d39077a708f3 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:22:56 -0500 Subject: [PATCH 18/41] misc(backup): condensed some code more --- backup/backup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 70dd513..a843726 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -62,8 +62,7 @@ class Backup(commands.Cog): async def backup_import(self, ctx: commands.Context): """Import your installed repositories and cogs from an export file.""" try: - export_raw = await ctx.message.attachments[0].read() - export = json.loads(export_raw) + export = json.loads(await ctx.message.attachments[0].read()) except (json.JSONDecodeError, IndexError): await ctx.send(error("Please provide a valid JSON export file.")) return From 3662ce3b2306a7042bb5691ecadd64df5b884a40 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:33:39 -0500 Subject: [PATCH 19/41] fix(backup): export branch as well --- backup/backup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backup/backup.py b/backup/backup.py index a843726..cc85708 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -44,6 +44,7 @@ class Backup(commands.Cog): repo_dict = { "name": repo.name, "url": repo.url, + "branch": repo.branch, "cogs": [] } From d7b5c5f2393b1ad5d0847029bdae561aed44da41 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 13:55:34 -0500 Subject: [PATCH 20/41] feat(backup): export more information on each individual cog --- backup/backup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index cc85708..f42a62a 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -52,7 +52,12 @@ class Backup(commands.Cog): for cog in cogs: if cog.repo_name == repo.name: - repo_dict["cogs"].append(cog.name) + cog_dict = { + "name": cog.name, + "pinned": cog.pinned, + "commit": cog.commit + } + repo_dict["cogs"].append(cog_dict) export_data.append(repo_dict) From fa6fcfb75f8932cc498b4eda72f59b359c041134 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:15:46 -0500 Subject: [PATCH 21/41] feat(backup): finished the importer, minus the loading functionality --- backup/backup.py | 115 ++++++++++++++++++++++++++++++++++++++++++++++- backup/info.json | 3 +- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index f42a62a..36f4f7a 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -5,12 +5,16 @@ # ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ | # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| +import contextlib +import logging import json import re from redbot.core import commands from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import error, text_to_file +from redbot.core.utils.chat_formatting import error, text_to_file, inline +from redbot.cogs.downloader.converters import InstalledCog +import redbot.cogs.downloader.errors as errors class Backup(commands.Cog): """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" @@ -21,6 +25,7 @@ class Backup(commands.Cog): def __init__(self, bot: Red): super().__init__() self.bot = bot + self.logger = logging.getLogger("red.sea.backup") @commands.group(autohelp=True) @commands.is_owner() @@ -77,3 +82,111 @@ class Backup(commands.Cog): if downloader is None: await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return + + repo_e = [] + uninstall_e = [] + install_e = [] + + async with ctx.typing(): + for repo in export: + # Most of this code is from the Downloader cog's repo_add funciton. + name = repo['name'] + branch = repo['branch'] + url = repo['url'] + cogs = repo['cogs'] + + if url.contains('PyLav/RedCogs'): + repo_e.append("PyLav cogs are not supported.") + continue + if name.startswith('.') or name.endswith('.'): + repo_e.append(f"Invalid repository name: {name}\nRepository names cannot start or end with a dot.") + continue + if re.match(r"^[a-zA-Z0-9_\-\.]+$", name) is None: + repo_e.append(f"Invalid repository name: {name}\nRepository names may only contain letters, numbers, underscores, hyphens, and dots.") + continue + + try: + repository = await downloader._repo_manager.add_repo(name, url, branch) # pylint: disable=protected-access + + except errors.ExistingGitRepo: + repo_e.append(f"Repository {name} already exists.") + continue + + except errors.AuthenticationError as err: + repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.") + self.log.exception( + "Something went wrong whilst cloning %s (to revision %s)", + url, + branch, + exc_info=err, + ) + continue + + except errors.CloningError as err: + repo_e.append(f"Cloning error while adding repository {name}. See logs for more information.") + self.log.exception( + "Something went wrong whilst cloning %s (to revision %s)", + url, + branch, + exc_info=err, + ) + continue + + except OSError: + repo_e.append(f"OS error while adding repository {name}. See logs for more information.") + self.log.exception( + "Something went wrong trying to add repo %s under name %s", + url, + name + ) + continue + + else: + cog_names = [] + for cog in cogs: + cog_names.append(cog['name']) + + for cog in set(cog_names): + poss_installed_path = (await downloader.cog_install_path()) / cog + if poss_installed_path.exists(): + with contextlib.suppress(commands.ExtensionNotLoaded): + await ctx.bot.unload_extension(cog) + await ctx.bot.remove_loaded_package(cog) + await downloader._delete_cog(poss_installed_path) + else: + uninstall_e.append(f"Failed to uninstall {cog}") + await downloader._remove_from_installed(cogs) + + for cog in cogs: + cog_name = cog['name'] + cog_pinned = cog['pinned'] + if cog_pinned: + commit = cog['commit'] + else: + commit = None + + async with repository.checkout(commit, exit_to_rev=repo.branch): + cogs, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access + if not cogs: + install_e.append(message) + continue + failed_reqs = await downloader._install_requirements(cogs) + if failed_reqs: + install_e.append(f"Failed to install {cog_name} due to missing requirements: {failed_reqs}") + continue + + installed_cogs, failed_cogs = await downloader._install_cogs(cogs) + + if repo.available_libraries: + installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) + + if cog_pinned: + for cog in installed_cogs: + cog.pinned = True + + await downloader._save_to_installed(installed_cogs + installed_libs) + if failed_cogs: + install_e.append(f"Failed to install {failed_cogs}") + if failed_libs: + install_e.append(f"Failed to install {failed_libs} required for {cog_name}") + await ctx.send("Import complete!\nErrors:", file=text_to_file(f"Repositories:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_e}\n\nInstalled Cogs:\n{install_e}", 'backup-errors.log')) diff --git a/backup/info.json b/backup/info.json index fa07072..29bbbb8 100644 --- a/backup/info.json +++ b/backup/info.json @@ -7,7 +7,8 @@ "end_user_data_statement" : "This cog does not store user information.", "hidden": false, "disabled": false, - "min_bot_version": "3.5.0", + "min_bot_version": "3.5.5", + "max_bot_version": "3.5.5", "min_python_version": [3, 10, 0], "tags": [] } From bbecff3ed006755d0a1b487c2b8ec910b684c895 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:16:18 -0500 Subject: [PATCH 22/41] fix(backup): forced incompatibility with pylav as intended --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 36f4f7a..8cf936a 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -95,7 +95,7 @@ class Backup(commands.Cog): url = repo['url'] cogs = repo['cogs'] - if url.contains('PyLav/RedCogs'): + if url.contains('PyLav/Red-Cogs'): repo_e.append("PyLav cogs are not supported.") continue if name.startswith('.') or name.endswith('.'): From add1e3ef1d822e1f6075df5937575bb818a8411c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:18:37 -0500 Subject: [PATCH 23/41] fix(backup): contains isn't a thing --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 8cf936a..6459572 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -95,7 +95,7 @@ class Backup(commands.Cog): url = repo['url'] cogs = repo['cogs'] - if url.contains('PyLav/Red-Cogs'): + if 'PyLav/Red-Cogs' in url: repo_e.append("PyLav cogs are not supported.") continue if name.startswith('.') or name.endswith('.'): From fdb5d323ebf5654644b64179b721efd33561fe29 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:21:54 -0500 Subject: [PATCH 24/41] fix(backup): commented out error handling for an error that doesn't exist yet --- backup/backup.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 6459572..624e27b 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -112,15 +112,17 @@ class Backup(commands.Cog): repo_e.append(f"Repository {name} already exists.") continue - except errors.AuthenticationError as err: - repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.") - self.log.exception( - "Something went wrong whilst cloning %s (to revision %s)", - url, - branch, - exc_info=err, - ) - continue + # This is commented out because errors.AuthenticationError is not yet implemented in Red 3.5.5's Downloader cog. + # Rather, it is only in the development version and will be added in version 3.5.6 (or whatever the next version is). + # except errors.AuthenticationError as err: + # repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.") + # self.log.exception( + # "Something went wrong whilst cloning %s (to revision %s)", + # url, + # branch, + # exc_info=err, + # ) + # continue except errors.CloningError as err: repo_e.append(f"Cloning error while adding repository {name}. See logs for more information.") From 6dfeb97180b945e9e187cb73244970d3e08f2d7c Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:22:46 -0500 Subject: [PATCH 25/41] fix(backup): logger, not log --- backup/backup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 624e27b..518a1bd 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -116,7 +116,7 @@ class Backup(commands.Cog): # Rather, it is only in the development version and will be added in version 3.5.6 (or whatever the next version is). # except errors.AuthenticationError as err: # repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.") - # self.log.exception( + # self.logger.exception( # "Something went wrong whilst cloning %s (to revision %s)", # url, # branch, @@ -126,7 +126,7 @@ class Backup(commands.Cog): except errors.CloningError as err: repo_e.append(f"Cloning error while adding repository {name}. See logs for more information.") - self.log.exception( + self.logger.exception( "Something went wrong whilst cloning %s (to revision %s)", url, branch, @@ -136,7 +136,7 @@ class Backup(commands.Cog): except OSError: repo_e.append(f"OS error while adding repository {name}. See logs for more information.") - self.log.exception( + self.logger.exception( "Something went wrong trying to add repo %s under name %s", url, name From 417ee9bde4e10b77d370bf4a805c157756edaebd Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:26:50 -0500 Subject: [PATCH 26/41] fix(backup): swapped around some kwargs for add_repo (L109) --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 518a1bd..d202f5c 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -106,7 +106,7 @@ class Backup(commands.Cog): continue try: - repository = await downloader._repo_manager.add_repo(name, url, branch) # pylint: disable=protected-access + repository = await downloader._repo_manager.add_repo(url, name, branch) # pylint: disable=protected-access except errors.ExistingGitRepo: repo_e.append(f"Repository {name} already exists.") From f4f174ce815c7ba1c8202f61e5512234c55637a5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:30:42 -0500 Subject: [PATCH 27/41] fix(backup): fixed a few errors --- backup/backup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index d202f5c..a21f285 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -110,7 +110,7 @@ class Backup(commands.Cog): except errors.ExistingGitRepo: repo_e.append(f"Repository {name} already exists.") - continue + repository = downloader._repo_manager.get_repo(name) # pylint: disable=protected-access # This is commented out because errors.AuthenticationError is not yet implemented in Red 3.5.5's Downloader cog. # Rather, it is only in the development version and will be added in version 3.5.6 (or whatever the next version is). @@ -157,7 +157,7 @@ class Backup(commands.Cog): await downloader._delete_cog(poss_installed_path) else: uninstall_e.append(f"Failed to uninstall {cog}") - await downloader._remove_from_installed(cogs) + await downloader._remove_from_installed(cog) for cog in cogs: cog_name = cog['name'] From 760059f8f0c6ac0f73fe1a1e1c8ec13ab28539da Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:37:51 -0500 Subject: [PATCH 28/41] fix(backup): fixed some stuff --- backup/backup.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index a21f285..add16f6 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -144,11 +144,16 @@ class Backup(commands.Cog): continue else: - cog_names = [] + cog_modules = [] for cog in cogs: - cog_names.append(cog['name']) + try: + cog_module = InstalledCog.convert(ctx, cog['name']) + except commands.BadArgument: + uninstall_e.append(f"Failed to uninstall {cog['name']}") + continue + cog_modules.append(cog_module) - for cog in set(cog_names): + for cog in set(cog.name for cog in cog_modules): poss_installed_path = (await downloader.cog_install_path()) / cog if poss_installed_path.exists(): with contextlib.suppress(commands.ExtensionNotLoaded): @@ -157,7 +162,7 @@ class Backup(commands.Cog): await downloader._delete_cog(poss_installed_path) else: uninstall_e.append(f"Failed to uninstall {cog}") - await downloader._remove_from_installed(cog) + await downloader._remove_from_installed(cog_modules) for cog in cogs: cog_name = cog['name'] @@ -168,16 +173,16 @@ class Backup(commands.Cog): commit = None async with repository.checkout(commit, exit_to_rev=repo.branch): - cogs, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access + cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access if not cogs: install_e.append(message) continue - failed_reqs = await downloader._install_requirements(cogs) + failed_reqs = await downloader._install_requirements(cogs_c) if failed_reqs: install_e.append(f"Failed to install {cog_name} due to missing requirements: {failed_reqs}") continue - installed_cogs, failed_cogs = await downloader._install_cogs(cogs) + installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) if repo.available_libraries: installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) From 8327270e612351c8c0f667f8b3cb45941939e36d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:38:26 -0500 Subject: [PATCH 29/41] fix(backup): awaited a coroutine --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index add16f6..84cc5b3 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -147,7 +147,7 @@ class Backup(commands.Cog): cog_modules = [] for cog in cogs: try: - cog_module = InstalledCog.convert(ctx, cog['name']) + cog_module = await InstalledCog.convert(ctx, cog['name']) except commands.BadArgument: uninstall_e.append(f"Failed to uninstall {cog['name']}") continue From acf89e090f3ceecdf3f9c7d6c86a67a02c499e26 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:40:58 -0500 Subject: [PATCH 30/41] fix(backup): fixed an AttributeError --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 84cc5b3..26c3860 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -172,7 +172,7 @@ class Backup(commands.Cog): else: commit = None - async with repository.checkout(commit, exit_to_rev=repo.branch): + async with repository.checkout(commit, exit_to_rev=repository.branch): cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access if not cogs: install_e.append(message) From 57c129c9ce0c4e917325a439169a28702f1e2c43 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:42:29 -0500 Subject: [PATCH 31/41] fix(backup): fixed another attributeerror --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 26c3860..bc69b62 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -184,7 +184,7 @@ class Backup(commands.Cog): installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) - if repo.available_libraries: + if repository.available_libraries: installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) if cog_pinned: From f33ebd29e9344746fa980a08334eb7a1655c89fc Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:45:58 -0500 Subject: [PATCH 32/41] fix(backup): fixed some other errors --- backup/backup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backup/backup.py b/backup/backup.py index bc69b62..9322105 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -186,6 +186,9 @@ class Backup(commands.Cog): if repository.available_libraries: installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) + else: + installed_libs = ((),) + failed_libs = () if cog_pinned: for cog in installed_cogs: From 9b930fc05bb251092b20f3fad755e83b7249fa7f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:48:07 -0500 Subject: [PATCH 33/41] fix(backup): fixed the final logging step --- backup/backup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 9322105..2d642c9 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -187,14 +187,14 @@ class Backup(commands.Cog): if repository.available_libraries: installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) else: - installed_libs = ((),) - failed_libs = () + installed_libs = None + failed_libs = None if cog_pinned: for cog in installed_cogs: cog.pinned = True - await downloader._save_to_installed(installed_cogs + installed_libs) + await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) if failed_cogs: install_e.append(f"Failed to install {failed_cogs}") if failed_libs: From 756265503c6e3a93708148a26bb59bf30e84741d Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 14:54:36 -0500 Subject: [PATCH 34/41] fix(backup): pylint fixes --- backup/backup.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 2d642c9..a69e0f0 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -12,9 +12,9 @@ import re from redbot.core import commands from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import error, text_to_file, inline +from redbot.cogs.downloader import errors from redbot.cogs.downloader.converters import InstalledCog -import redbot.cogs.downloader.errors as errors +from redbot.core.utils.chat_formatting import error, text_to_file class Backup(commands.Cog): """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" @@ -89,7 +89,7 @@ class Backup(commands.Cog): async with ctx.typing(): for repo in export: - # Most of this code is from the Downloader cog's repo_add funciton. + # Most of this code is from the Downloader cog. name = repo['name'] branch = repo['branch'] url = repo['url'] @@ -159,10 +159,10 @@ class Backup(commands.Cog): with contextlib.suppress(commands.ExtensionNotLoaded): await ctx.bot.unload_extension(cog) await ctx.bot.remove_loaded_package(cog) - await downloader._delete_cog(poss_installed_path) + await downloader._delete_cog(poss_installed_path) # pylint: disable=protected-access else: uninstall_e.append(f"Failed to uninstall {cog}") - await downloader._remove_from_installed(cog_modules) + await downloader._remove_from_installed(cog_modules) # pylint: disable=protected-access for cog in cogs: cog_name = cog['name'] @@ -177,12 +177,12 @@ class Backup(commands.Cog): if not cogs: install_e.append(message) continue - failed_reqs = await downloader._install_requirements(cogs_c) + failed_reqs = await downloader._install_requirements(cogs_c) # pylint: disable=protected-access if failed_reqs: install_e.append(f"Failed to install {cog_name} due to missing requirements: {failed_reqs}") continue - installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) + installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) # pylint: disable=protected-access if repository.available_libraries: installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) @@ -194,7 +194,7 @@ class Backup(commands.Cog): for cog in installed_cogs: cog.pinned = True - await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) + await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) # pylint: disable=protected-access if failed_cogs: install_e.append(f"Failed to install {failed_cogs}") if failed_libs: From a00bfc3f93023b6367634bf7a8017afed7174ea5 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:00:46 -0500 Subject: [PATCH 35/41] feat(backup): added more verbose logging --- backup/backup.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index a69e0f0..318b02c 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -83,6 +83,9 @@ class Backup(commands.Cog): await ctx.send(error(f"You do not have the `Downloader` cog loaded. Please run `{ctx.prefix}load downloader` and try again.")) return + repo_s = [] + uninstall_s = [] + install_s = [] repo_e = [] uninstall_e = [] install_e = [] @@ -107,6 +110,7 @@ class Backup(commands.Cog): try: repository = await downloader._repo_manager.add_repo(url, name, branch) # pylint: disable=protected-access + repo_s.append(f"Added repository {name} from {url} on branch {branch}.") except errors.ExistingGitRepo: repo_e.append(f"Repository {name} already exists.") @@ -160,6 +164,7 @@ class Backup(commands.Cog): await ctx.bot.unload_extension(cog) await ctx.bot.remove_loaded_package(cog) await downloader._delete_cog(poss_installed_path) # pylint: disable=protected-access + uninstall_s.append(f"Uninstalled {cog}") else: uninstall_e.append(f"Failed to uninstall {cog}") await downloader._remove_from_installed(cog_modules) # pylint: disable=protected-access @@ -174,7 +179,7 @@ class Backup(commands.Cog): async with repository.checkout(commit, exit_to_rev=repository.branch): cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access - if not cogs: + if not cogs_c: install_e.append(message) continue failed_reqs = await downloader._install_requirements(cogs_c) # pylint: disable=protected-access @@ -195,8 +200,12 @@ class Backup(commands.Cog): cog.pinned = True await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) # pylint: disable=protected-access + if installed_cogs: + install_s.append(f"Installed {installed_cogs}") + if installed_libs: + install_s.append(f"Installed {installed_libs} required for {cog_name}") if failed_cogs: install_e.append(f"Failed to install {failed_cogs}") if failed_libs: install_e.append(f"Failed to install {failed_libs} required for {cog_name}") - await ctx.send("Import complete!\nErrors:", file=text_to_file(f"Repositories:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_e}\n\nInstalled Cogs:\n{install_e}", 'backup-errors.log')) + await ctx.send("Import complete!", file=text_to_file(f"Repositories:\n{repo_s}\n\nRepository Errors:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_s}\n\nUninstalled Cogs Errors:\n{uninstall_e}\n\nInstalled Cogs:\n{install_s}\n\nInstalled Cogs Errors:{install_e}", 'backup.log')) From 7766b94a9e4482eec5bc29623d598174aa17b346 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:02:06 -0500 Subject: [PATCH 36/41] fix(backup): added a missing newline --- backup/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/backup.py b/backup/backup.py index 318b02c..2c30722 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -208,4 +208,4 @@ class Backup(commands.Cog): install_e.append(f"Failed to install {failed_cogs}") if failed_libs: install_e.append(f"Failed to install {failed_libs} required for {cog_name}") - await ctx.send("Import complete!", file=text_to_file(f"Repositories:\n{repo_s}\n\nRepository Errors:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_s}\n\nUninstalled Cogs Errors:\n{uninstall_e}\n\nInstalled Cogs:\n{install_s}\n\nInstalled Cogs Errors:{install_e}", 'backup.log')) + await ctx.send("Import complete!", file=text_to_file(f"Repositories:\n{repo_s}\n\nRepository Errors:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_s}\n\nUninstalled Cogs Errors:\n{uninstall_e}\n\nInstalled Cogs:\n{install_s}\n\nInstalled Cogs Errors:\n{install_e}", 'backup.log')) From f57b2c51965a221c12a9702edb592bba05b89163 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:05:25 -0500 Subject: [PATCH 37/41] fix(backup): fixed some logging stuff --- backup/backup.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 2c30722..610417a 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -201,11 +201,15 @@ class Backup(commands.Cog): await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) # pylint: disable=protected-access if installed_cogs: - install_s.append(f"Installed {installed_cogs}") + installed_cog_name = installed_cogs[0].name + install_s.append(f"Installed {installed_cog_name}") if installed_libs: - install_s.append(f"Installed {installed_libs} required for {cog_name}") + for lib in installed_libs: + install_s.append(f"Installed {lib.name} required for {cog_name}") if failed_cogs: - install_e.append(f"Failed to install {failed_cogs}") + failed_cog_name = failed_cogs[0].name + install_e.append(f"Failed to install {failed_cog_name}") if failed_libs: - install_e.append(f"Failed to install {failed_libs} required for {cog_name}") + for lib in failed_libs: + install_e.append(f"Failed to install {lib.name} required for {cog_name}") await ctx.send("Import complete!", file=text_to_file(f"Repositories:\n{repo_s}\n\nRepository Errors:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_s}\n\nUninstalled Cogs Errors:\n{uninstall_e}\n\nInstalled Cogs:\n{install_s}\n\nInstalled Cogs Errors:\n{install_e}", 'backup.log')) From 4e70145d38731e31dce95601127841d7de96e74f Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:12:34 -0500 Subject: [PATCH 38/41] feat(backup): exports will now check if a cog is loaded --- backup/backup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backup/backup.py b/backup/backup.py index 610417a..2fe0946 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -59,6 +59,7 @@ class Backup(commands.Cog): if cog.repo_name == repo.name: cog_dict = { "name": cog.name, + "loaded": ctx.bot.get_cog(cog.name) is not None, "pinned": cog.pinned, "commit": cog.commit } From 22237a21b1e968b88130bd99cc41fe491ac2bbcc Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:48:23 -0500 Subject: [PATCH 39/41] fix(backup): doing some cleanup --- .docs/backup.md | 4 ++-- backup/backup.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.docs/backup.md b/.docs/backup.md index 787d1db..550f7da 100644 --- a/.docs/backup.md +++ b/.docs/backup.md @@ -20,6 +20,6 @@ Exports a JSON list of all of your added repositories, and their installed cogs. ### backup import -- Usage: `[p]backup import [json]` +- Usage: `[p]backup import` -Imports, reinstalls, and reloads cogs from a valid export. Requires the JSON export to be attached to the invoking message as an attachment. Ignores itself and PyLav cogs. +Reinstalls repositories and cogs from a valid export. Requires the JSON export to be attached to the invoking message as an attachment. Ignores itself and PyLav cogs, due to possible conflicts with Docker images. diff --git a/backup/backup.py b/backup/backup.py index 2fe0946..8c3b9c8 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -59,7 +59,9 @@ class Backup(commands.Cog): if cog.repo_name == repo.name: cog_dict = { "name": cog.name, - "loaded": ctx.bot.get_cog(cog.name) is not None, + # "loaded": cog.name in ctx.bot.extensions.keys(), + # this functionality was planned but never implemented due to Red limitations + # and the possibility of restoration functionality being added to Core "pinned": cog.pinned, "commit": cog.commit } @@ -178,6 +180,10 @@ class Backup(commands.Cog): else: commit = None + # If you're forking this cog, make sure to change these strings! + if cog_name == 'backup' and 'SeaswimmerTheFsh/SeaCogs' in url: + continue + async with repository.checkout(commit, exit_to_rev=repository.branch): cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access if not cogs_c: From 49907e78035d9ca0e76013dce0c00f41797d5df9 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:52:44 -0500 Subject: [PATCH 40/41] fix(backup): fixed not installing cogs from repositories that already exist --- backup/backup.py | 125 +++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/backup/backup.py b/backup/backup.py index 8c3b9c8..821ed0c 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -150,73 +150,72 @@ class Backup(commands.Cog): ) continue - else: - cog_modules = [] - for cog in cogs: - try: - cog_module = await InstalledCog.convert(ctx, cog['name']) - except commands.BadArgument: - uninstall_e.append(f"Failed to uninstall {cog['name']}") + cog_modules = [] + for cog in cogs: + try: + cog_module = await InstalledCog.convert(ctx, cog['name']) + except commands.BadArgument: + uninstall_e.append(f"Failed to uninstall {cog['name']}") + continue + cog_modules.append(cog_module) + + for cog in set(cog.name for cog in cog_modules): + poss_installed_path = (await downloader.cog_install_path()) / cog + if poss_installed_path.exists(): + with contextlib.suppress(commands.ExtensionNotLoaded): + await ctx.bot.unload_extension(cog) + await ctx.bot.remove_loaded_package(cog) + await downloader._delete_cog(poss_installed_path) # pylint: disable=protected-access + uninstall_s.append(f"Uninstalled {cog}") + else: + uninstall_e.append(f"Failed to uninstall {cog}") + await downloader._remove_from_installed(cog_modules) # pylint: disable=protected-access + + for cog in cogs: + cog_name = cog['name'] + cog_pinned = cog['pinned'] + if cog_pinned: + commit = cog['commit'] + else: + commit = None + + # If you're forking this cog, make sure to change these strings! + if cog_name == 'backup' and 'SeaswimmerTheFsh/SeaCogs' in url: + continue + + async with repository.checkout(commit, exit_to_rev=repository.branch): + cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access + if not cogs_c: + install_e.append(message) + continue + failed_reqs = await downloader._install_requirements(cogs_c) # pylint: disable=protected-access + if failed_reqs: + install_e.append(f"Failed to install {cog_name} due to missing requirements: {failed_reqs}") continue - cog_modules.append(cog_module) - for cog in set(cog.name for cog in cog_modules): - poss_installed_path = (await downloader.cog_install_path()) / cog - if poss_installed_path.exists(): - with contextlib.suppress(commands.ExtensionNotLoaded): - await ctx.bot.unload_extension(cog) - await ctx.bot.remove_loaded_package(cog) - await downloader._delete_cog(poss_installed_path) # pylint: disable=protected-access - uninstall_s.append(f"Uninstalled {cog}") + installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) # pylint: disable=protected-access + + if repository.available_libraries: + installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) else: - uninstall_e.append(f"Failed to uninstall {cog}") - await downloader._remove_from_installed(cog_modules) # pylint: disable=protected-access + installed_libs = None + failed_libs = None - for cog in cogs: - cog_name = cog['name'] - cog_pinned = cog['pinned'] if cog_pinned: - commit = cog['commit'] - else: - commit = None + for cog in installed_cogs: + cog.pinned = True - # If you're forking this cog, make sure to change these strings! - if cog_name == 'backup' and 'SeaswimmerTheFsh/SeaCogs' in url: - continue - - async with repository.checkout(commit, exit_to_rev=repository.branch): - cogs_c, message = await downloader._filter_incorrect_cogs_by_names(repository, [cog_name]) # pylint: disable=protected-access - if not cogs_c: - install_e.append(message) - continue - failed_reqs = await downloader._install_requirements(cogs_c) # pylint: disable=protected-access - if failed_reqs: - install_e.append(f"Failed to install {cog_name} due to missing requirements: {failed_reqs}") - continue - - installed_cogs, failed_cogs = await downloader._install_cogs(cogs_c) # pylint: disable=protected-access - - if repository.available_libraries: - installed_libs, failed_libs = await repository.install_libraries(target_dir=downloader.SHAREDLIB_PATH, req_target_dir=downloader.LIB_PATH) - else: - installed_libs = None - failed_libs = None - - if cog_pinned: - for cog in installed_cogs: - cog.pinned = True - - await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) # pylint: disable=protected-access - if installed_cogs: - installed_cog_name = installed_cogs[0].name - install_s.append(f"Installed {installed_cog_name}") - if installed_libs: - for lib in installed_libs: - install_s.append(f"Installed {lib.name} required for {cog_name}") - if failed_cogs: - failed_cog_name = failed_cogs[0].name - install_e.append(f"Failed to install {failed_cog_name}") - if failed_libs: - for lib in failed_libs: - install_e.append(f"Failed to install {lib.name} required for {cog_name}") + await downloader._save_to_installed(installed_cogs + installed_libs if installed_libs else installed_cogs) # pylint: disable=protected-access + if installed_cogs: + installed_cog_name = installed_cogs[0].name + install_s.append(f"Installed {installed_cog_name}") + if installed_libs: + for lib in installed_libs: + install_s.append(f"Installed {lib.name} required for {cog_name}") + if failed_cogs: + failed_cog_name = failed_cogs[0].name + install_e.append(f"Failed to install {failed_cog_name}") + if failed_libs: + for lib in failed_libs: + install_e.append(f"Failed to install {lib.name} required for {cog_name}") await ctx.send("Import complete!", file=text_to_file(f"Repositories:\n{repo_s}\n\nRepository Errors:\n{repo_e}\n\nUninstalled Cogs:\n{uninstall_s}\n\nUninstalled Cogs Errors:\n{uninstall_e}\n\nInstalled Cogs:\n{install_s}\n\nInstalled Cogs Errors:\n{install_e}", 'backup.log')) From 053b5b747228ffae4ca65444b866473d0e91f4a6 Mon Sep 17 00:00:00 2001 From: SeaswimmerTheFsh Date: Wed, 31 Jan 2024 15:56:28 -0500 Subject: [PATCH 41/41] fix(backup): do not uninstall yourself --- backup/backup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backup/backup.py b/backup/backup.py index 821ed0c..ffa2238 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -152,6 +152,9 @@ class Backup(commands.Cog): cog_modules = [] for cog in cogs: + # If you're forking this cog, make sure to change these strings! + if cog['name'] == "backup" and 'SeaswimmerTheFsh/SeaCogs' in url: + continue try: cog_module = await InstalledCog.convert(ctx, cog['name']) except commands.BadArgument: