Repository: Cog-Creators/Red-DiscordBot

Issue:
[V3 Launcher] Unable to [6]Remove Instance on Windows
### Type:

- [ ] Suggestion
- [x] Bug

### Brief description of the problem
Unable to remove a v3 instance using red-launcher

### Expected behavior
Bot would be removed successfully

### Actual behavior
https://cdn.discordapp.com/attachments/387398816317440000/420967871678775306/unknown.png

### Steps to reproduce

1.  Create RED instance with default settings on windows
2.  Install any cogs _(maybe unneeded)_
3.  Use red-launcher to remove the instance



Discussion:

Resolving patch:
diff --git a/redbot/cogs/downloader/repo_manager.py b/redbot/cogs/downloader/repo_manager.py
--- a/redbot/cogs/downloader/repo_manager.py
+++ b/redbot/cogs/downloader/repo_manager.py
@@ -13,6 +13,7 @@
 
 from redbot.core import Config
 from redbot.core import data_manager
+from redbot.core.utils import safe_delete
 from .errors import *
 from .installable import Installable, InstallableType
 from .json_mixins import RepoJSONMixin
@@ -614,7 +615,7 @@ async def delete_repo(self, name: str):
         if repo is None:
             raise MissingGitRepo("There is no repo with the name {}".format(name))
 
-        shutil.rmtree(str(repo.folder_path))
+        safe_delete(repo.folder_path)
 
         try:
             del self._repos[name]
diff --git a/redbot/core/utils/__init__.py b/redbot/core/utils/__init__.py
--- a/redbot/core/utils/__init__.py
+++ b/redbot/core/utils/__init__.py
@@ -1,4 +1,8 @@
-__all__ = ['TYPE_CHECKING', 'NewType']
+__all__ = ['TYPE_CHECKING', 'NewType', 'safe_delete']
+
+from pathlib import Path
+import os
+import shutil
 
 try:
     from typing import TYPE_CHECKING
@@ -10,3 +14,12 @@
 except ImportError:
     def NewType(name, tp):
         return type(name, (tp,), {})
+
+
+def safe_delete(pth: Path):
+    if pth.exists():
+        for root, dirs, files in os.walk(str(pth)):
+            os.chmod(root, 0o755)
+            for d in dirs:
+                os.chmod(os.path.join(root, d), 0o755)
+        shutil.rmtree(str(pth), ignore_errors=True)
diff --git a/redbot/setup.py b/redbot/setup.py
--- a/redbot/setup.py
+++ b/redbot/setup.py
@@ -15,6 +15,7 @@
 from redbot.core.cli import confirm
 from redbot.core.data_manager import basic_config_default
 from redbot.core.json_io import JsonIO
+from redbot.core.utils import safe_delete
 
 config_dir = None
 appdir = appdirs.AppDirs("Red-DiscordBot")
@@ -310,31 +311,26 @@ def remove_instance():
                 selected, dt.utcnow().strftime("%Y-%m-%d %H-%M-%S")
             )
             pth = Path(instance_data["DATA_PATH"])
-            home = pth.home()
-            backup_file = home / backup_filename
-            os.chdir(str(pth.parent))  # str is used here because 3.5 support
-            with tarfile.open(str(backup_file), "w:gz") as tar:
-                tar.add(pth.stem)  # add all files in that directory
-            print(
-                "A backup of {} has been made. It is at {}".format(
-                    selected, backup_file
+            if pth.exists():
+                home = pth.home()
+                backup_file = home / backup_filename
+                os.chdir(str(pth.parent))  # str is used here because 3.5 support
+                with tarfile.open(str(backup_file), "w:gz") as tar:
+                    tar.add(pth.stem)  # add all files in that directory
+                print(
+                    "A backup of {} has been made. It is at {}".format(
+                        selected, backup_file
+                    )
                 )
-            )
-            print("Removing the instance...")
-            try:
-                shutil.rmtree(str(pth))
-            except FileNotFoundError:
-                pass  # data dir was removed manually
+                print("Removing the instance...")
+                safe_delete(pth)
             save_config(selected, {}, remove=True)
             print("The instance has been removed")
             return
     else:
         pth = Path(instance_data["DATA_PATH"])
         print("Removing the instance...")
-        try:
-            shutil.rmtree(str(pth))
-        except FileNotFoundError:
-            pass  # data dir was removed manually
+        safe_delete(pth)
         save_config(selected, {}, remove=True)
         print("The instance has been removed")
         return


Test patch: