[V3 Launcher] Reinstall Red option (#1536)

* [V3 Launcher] Reinstall Red option

* [V3 Setup] Divided remove_instance function

* Removing changes from another PR

* Indent fails fix

* use remove_instance_interaction for --delete

* Fix some issues with remove_instance

removed `index: int` because what's being passed there is a string
data -> instance_data

* bug fixes, working version
This commit is contained in:
retke 2018-05-04 08:01:37 +02:00 committed by Kowlin
parent 23192b9ef6
commit 95ef5d6348
2 changed files with 171 additions and 102 deletions

View File

@ -7,7 +7,10 @@ import argparse
import asyncio
import pkg_resources
from redbot.setup import basic_setup, load_existing_config, remove_instance
from pathlib import Path
from redbot.setup import basic_setup, load_existing_config, remove_instance, remove_instance_interaction, create_backup, save_config
from redbot.core.utils import safe_delete
from redbot.core.cli import confirm
if sys.platform == "linux":
import distro
@ -60,7 +63,7 @@ def parse_cli_args():
return parser.parse_known_args()
def update_red(dev=False, voice=False, mongo=False, docs=False, test=False):
def update_red(dev=False, reinstall=False, voice=False, mongo=False, docs=False, test=False):
interpreter = sys.executable
print("Updating Red...")
# If the user ran redbot-launcher.exe, updating with pip will fail
@ -93,12 +96,21 @@ def update_red(dev=False, voice=False, mongo=False, docs=False, test=False):
package = "Red-DiscordBot"
if egg_l:
package += "[{}]".format(", ".join(egg_l))
code = subprocess.call([
interpreter, "-m",
"pip", "install", "-U",
"--process-dependency-links",
package
])
if reinstall:
code = subprocess.call([
interpreter, "-m",
"pip", "install", "-U", "-I",
"--force-reinstall", "--no-cache-dir",
"--process-dependency-links",
package
])
else:
code = subprocess.call([
interpreter, "-m",
"pip", "install", "-U",
"--process-dependency-links",
package
])
if code == 0:
print("Red has been updated")
else:
@ -223,6 +235,37 @@ def instance_menu():
return name_num_map[str(selection)]
async def reset_red():
instances = load_existing_config()
if not instances:
print("No instance to delete.\n")
return
print("WARNING: You are about to remove ALL Red instances on this computer.")
print("If you want to reset data of only one instance, "
"please select option 5 in the launcher.")
await asyncio.sleep(2)
print("\nIf you continue you will remove these instanes.\n")
for instance in list(instances.keys()):
print(" - {}".format(instance))
await asyncio.sleep(3)
print('\nIf you want to reset all instances, type "I agree".')
response = input("> ").strip()
if response != "I agree":
print("Cancelling...")
return
if confirm("\nDo you want to create a backup for an instance? (y/n) "):
for index, instance in instances.items():
print("\nRemoving {}...".format(index))
await create_backup(index, instance)
await remove_instance(index, instance)
else:
for index, instance in instances.items():
await remove_instance(index, instance)
print("All instances have been removed.")
def clear_screen():
if IS_WINDOWS:
os.system("cls")
@ -247,6 +290,33 @@ def extras_selector():
return selected
def development_choice(reinstall = False):
while True:
print("\n")
print("Do you want to install stable or development version?")
print("1. Stable version")
print("2. Development version")
choice = user_choice()
print("\n")
selected = extras_selector()
if choice == "1":
update_red(
dev=False, reinstall=reinstall, voice=True if "voice" in selected else False,
docs=True if "docs" in selected else False,
test=True if "test" in selected else False,
mongo=True if "mongo" in selected else False
)
break
elif choice == "2":
update_red(
dev=True, reinstall=reinstall, voice=True if "voice" in selected else False,
docs=True if "docs" in selected else False,
test=True if "test" in selected else False,
mongo=True if "mongo" in selected else False
)
break
def debug_info():
pyver = sys.version
redver = pkg_resources.get_distribution("Red-DiscordBot").version
@ -275,55 +345,64 @@ def debug_info():
def main_menu():
if IS_WINDOWS:
os.system("TITLE Red - Discord Bot V3 Launcher")
clear_screen()
while True:
print(INTRO)
print("1. Run Red w/ autorestart in case of issues")
print("2. Run Red")
print("3. Update Red")
print("4. Update Red (development version)")
print("5. Create Instance")
print("6. Remove Instance")
print("7. Debug information (use this if having issues with the launcher or bot)")
print("4. Create Instance")
print("5. Remove Instance")
print("6. Debug information (use this if having issues with the launcher or bot)")
print("7. Reinstall Red")
print("0. Exit")
choice = user_choice()
if choice == "1":
instance = instance_menu()
cli_flags = cli_flag_getter()
if instance:
cli_flags = cli_flag_getter()
run_red(instance, autorestart=True, cliflags=cli_flags)
wait()
elif choice == "2":
instance = instance_menu()
cli_flags = cli_flag_getter()
if instance:
cli_flags = cli_flag_getter()
run_red(instance, autorestart=False, cliflags=cli_flags)
wait()
elif choice == "3":
selected = extras_selector()
update_red(
dev=False, voice=True if "voice" in selected else False,
docs=True if "docs" in selected else False,
test=True if "test" in selected else False,
mongo=True if "mongo" in selected else False
)
development_choice()
wait()
elif choice == "4":
selected = extras_selector()
update_red(
dev=True, voice=True if "voice" in selected else False,
docs=True if "docs" in selected else False,
test=True if "test" in selected else False,
mongo=True if "mongo" in selected else False
)
wait()
elif choice == "5":
basic_setup()
wait()
elif choice == "6":
asyncio.get_event_loop().run_until_complete(remove_instance())
elif choice == "5":
asyncio.get_event_loop().run_until_complete(remove_instance_interaction())
wait()
elif choice == "7":
elif choice == "6":
debug_info()
elif choice == "7":
while True:
loop = asyncio.get_event_loop()
clear_screen()
print("==== Reinstall Red ====")
print("1. Reinstall Red requirements (discard code changes, keep data and 3rd party cogs)")
print("2. Reset all data")
print("3. Factory reset (discard code changes, reset all data)")
print("\n")
print("0. Back")
choice = user_choice()
if choice == "1":
development_choice(reinstall=True)
wait()
elif choice == "2":
loop.run_until_complete(reset_red())
wait()
elif choice == "3":
loop.run_until_complete(reset_red())
development_choice(reinstall=True)
wait()
elif choice == "0":
break
elif choice == "0":
break
clear_screen()

View File

@ -302,7 +302,61 @@ async def edit_instance():
)
async def remove_instance():
async def create_backup(selected, instance_data):
if confirm("Would you like to make a backup of the data for this instance? (y/n)"):
if instance_data["STORAGE_TYPE"] == "MongoDB":
print("Backing up the instance's data...")
await mongo_to_json(instance_data["DATA_PATH"], instance_data["STORAGE_DETAILS"])
backup_filename = "redv3-{}-{}.tar.gz".format(
selected, dt.utcnow().strftime("%Y-%m-%d %H-%M-%S")
)
pth = Path(instance_data["DATA_PATH"])
if pth.exists():
home = pth.home()
backup_file = home / backup_filename
os.chdir(str(pth.parent))
with tarfile.open(str(backup_file), "w:gz") as tar:
tar.add(pth.stem)
print("A backup of {} has been made. It is at {}".format(
selected, backup_file
))
else:
print("Backing up the instance's data...")
backup_filename = "redv3-{}-{}.tar.gz".format(
selected, dt.utcnow().strftime("%Y-%m-%d %H-%M-%S")
)
pth = Path(instance_data["DATA_PATH"])
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
)
)
async def remove_instance(selected, instance_data):
instance_list = load_existing_config()
if instance_data["STORAGE_TYPE"] == "MongoDB":
m = Mongo("Core", **instance_data["STORAGE_DETAILS"])
db = m.db
collections = await db.collection_names(include_system_collections=False)
for name in collections:
collection = await db.get_collection(name)
await collection.drop()
else:
pth = Path(instance_data["DATA_PATH"])
safe_delete(pth)
save_config(selected, {}, remove=True)
print("The instance {} has been removed\n".format(selected))
async def remove_instance_interaction():
instance_list = load_existing_config()
if not instance_list:
print("No instances have been set up!")
@ -321,79 +375,15 @@ async def remove_instance():
print("That isn't a valid instance!")
return
instance_data = instance_list[selected]
if confirm("Would you like to make a backup of the data for this instance? (y/n)"):
if instance_data["STORAGE_TYPE"] == "MongoDB":
print("Backing up the instance's data...")
await mongo_to_json(instance_data["DATA_PATH"], instance_data["STORAGE_DETAILS"])
backup_filename = "redv3-{}-{}.tar.gz".format(
selected, dt.utcnow().strftime("%Y-%m-%d %H-%M-%S")
)
pth = Path(instance_data["DATA_PATH"])
if pth.exists():
home = pth.home()
backup_file = home / backup_filename
os.chdir(str(pth.parent))
with tarfile.open(str(backup_file), "w:gz") as tar:
tar.add(pth.stem)
print("A backup of {} has been made. It is at {}".format(
selected, backup_file
))
print("Removing the instance...")
m = Mongo("Core", **instance_data["STORAGE_DETAILS"])
db = m.db
collections = await db.collection_names(include_system_collections=False)
for name in collections:
collection = await db.get_collection(name)
await collection.drop()
safe_delete(pth)
save_config(selected, {}, remove=True)
print("The instance has been removed.")
return
else:
print("Backing up the instance's data...")
backup_filename = "redv3-{}-{}.tar.gz".format(
selected, dt.utcnow().strftime("%Y-%m-%d %H-%M-%S")
)
pth = Path(instance_data["DATA_PATH"])
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...")
safe_delete(pth)
save_config(selected, {}, remove=True)
print("The instance has been removed")
return
else:
print("Removing the instance...")
if instance_data["STORAGE_TYPE"] == "MongoDB":
m = Mongo("Core", **instance_data["STORAGE_DETAILS"])
db = m.db
collections = await db.collection_names(include_system_collections=False)
for name in collections:
collection = await db.get_collection(name)
await collection.drop()
else:
pth = Path(instance_data["DATA_PATH"])
safe_delete(pth)
save_config(selected, {}, remove=True)
print("The instance has been removed")
return
await create_backup(selected, instance_data)
await remove_instance(selected, instance_data)
def main():
if args.delete:
loop = asyncio.get_event_loop()
loop.run_until_complete(remove_instance())
loop.run_until_complete(remove_instance_interaction())
elif args.edit:
loop = asyncio.get_event_loop()
loop.run_until_complete(edit_instance())