Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Campaign to Save the CLI #161

Merged
merged 18 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import logging
import threading
import queue

import config
import installer
import msg
import utils


class CLI:
def __init__(self):
self.running = True
self.choice_q = queue.Queue()
self.input_q = queue.Queue()
self.event = threading.Event()

def stop(self):
self.running = False

def run(self):
config.DIALOG = "cli"

self.thread = utils.start_thread(installer.ensure_launcher_shortcuts, daemon_bool=True, app=self)

while self.running:
self.user_input_processor()

msg.logos_msg("Exiting CLI installer.")


def user_input_processor(self):
prompt = None
question = None
options = None
choice = None
if self.input_q.qsize() > 0:
prompt = self.input_q.get()
if prompt is not None and isinstance(prompt, tuple):
question = prompt[0]
options = prompt[1]
if question is not None and options is not None:
choice = input(f"{question}: {options}: ")
if choice is not None and choice.lower() == 'exit':
self.running = False
if choice is not None:
self.choice_q.put(choice)
self.event.set()


def command_line_interface():
CLI().run()
1 change: 1 addition & 0 deletions control.py
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixed in #182

Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ def copy_data(src_dirs, dst_dir):

def remove_install_dir():
folder = Path(config.INSTALLDIR)
# FIXME: msg.cli_question needs additional arg
if (
folder.is_dir()
and msg.cli_question(f"Delete \"{folder}\" and all its contents?")
Expand Down
168 changes: 109 additions & 59 deletions installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
# To replicate, start a TUI install, return/cancel on second step
# Then launch a new install

# TODO: Reimplement `--install-app`?


def ensure_product_choice(app=None):
config.INSTALL_STEPS_COUNT += 1
Expand All @@ -27,13 +25,16 @@ def ensure_product_choice(app=None):

if not config.FLPRODUCT:
if app:
utils.send_task(app, 'FLPRODUCT')
if config.DIALOG == 'curses':
app.product_e.wait()
config.FLPRODUCT = app.product_q.get()
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
if config.DIALOG == 'cli':
app.input_q.put(("Choose which FaithLife product the script should install: ", ["Logos", "Verbum", "Exit"]))
app.event.wait()
app.event.clear()
config.FLPRODUCT = app.choice_q.get()
else:
utils.send_task(app, 'FLPRODUCT')
if config.DIALOG == 'curses':
app.product_e.wait()
config.FLPRODUCT = app.product_q.get()
else:
if config.DIALOG == 'curses':
app.set_product(config.FLPRODUCT)
Expand All @@ -58,13 +59,16 @@ def ensure_version_choice(app=None):
logging.debug('- config.TARGETVERSION')
if not config.TARGETVERSION:
if app:
utils.send_task(app, 'TARGETVERSION')
if config.DIALOG == 'curses':
app.version_e.wait()
config.TARGETVERSION = app.version_q.get()
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
if config.DIALOG == 'cli':
app.input_q.put((f"Which version of {config.FLPRODUCT} should the script install?: ", ["10", "9", "Exit"]))
app.event.wait()
app.event.clear()
config.TARGETVERSION = app.choice_q.get()
else:
utils.send_task(app, 'TARGETVERSION')
if config.DIALOG == 'curses':
app.version_e.wait()
config.TARGETVERSION = app.version_q.get()
else:
if config.DIALOG == 'curses':
app.set_version(config.TARGETVERSION)
Expand All @@ -81,14 +85,19 @@ def ensure_release_choice(app=None):

if not config.TARGET_RELEASE_VERSION:
if app:
utils.send_task(app, 'TARGET_RELEASE_VERSION')
if config.DIALOG == 'curses':
app.release_e.wait()
config.TARGET_RELEASE_VERSION = app.release_q.get()
logging.debug(f"{config.TARGET_RELEASE_VERSION=}")
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
if config.DIALOG == 'cli':
utils.start_thread(network.get_logos_releases, daemon_bool=True, app=app)
app.event.wait()
app.event.clear()
app.event.wait() # Wait for user input queue to receive input
app.event.clear()
config.TARGET_RELEASE_VERSION = app.choice_q.get()
else:
utils.send_task(app, 'TARGET_RELEASE_VERSION')
if config.DIALOG == 'curses':
app.release_e.wait()
config.TARGET_RELEASE_VERSION = app.release_q.get()
logging.debug(f"{config.TARGET_RELEASE_VERSION=}")
else:
if config.DIALOG == 'curses':
app.set_release(config.TARGET_RELEASE_VERSION)
Expand All @@ -109,17 +118,20 @@ def ensure_install_dir_choice(app=None):
default = f"{str(Path.home())}/{config.FLPRODUCT}Bible{config.TARGETVERSION}" # noqa: E501
if not config.INSTALLDIR:
if app:
if config.DIALOG == 'tk':
if config.DIALOG == 'cli':
default = f"{str(Path.home())}/{config.FLPRODUCT}Bible{config.TARGETVERSION}" # noqa: E501
question = f"Where should {config.FLPRODUCT} files be installed to?: " # noqa: E501
app.input_q.put((question, [default, "Type your own custom path", "Exit"]))
app.event.wait()
app.event.clear()
config.INSTALLDIR = app.choice_q.get()
elif config.DIALOG == 'tk':
config.INSTALLDIR = default
config.APPDIR_BINDIR = f"{config.INSTALLDIR}/data/bin"
elif config.DIALOG == 'curses':
utils.send_task(app, 'INSTALLDIR')
app.installdir_e.wait()
config.INSTALLDIR = app.installdir_q.get()
config.APPDIR_BINDIR = f"{config.INSTALLDIR}/data/bin"
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
config.APPDIR_BINDIR = f"{config.INSTALLDIR}/data/bin"
else:
if config.DIALOG == 'curses':
app.set_installdir(config.INSTALLDIR)
Expand All @@ -143,13 +155,23 @@ def ensure_wine_choice(app=None):
if utils.get_wine_exe_path() is None:
network.set_recommended_appimage_config()
if app:
utils.send_task(app, 'WINE_EXE')
if config.DIALOG == 'curses':
app.wine_e.wait()
config.WINE_EXE = app.wine_q.get()
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
if config.DIALOG == 'cli':
options = utils.get_wine_options(
utils.find_appimage_files(config.TARGET_RELEASE_VERSION),
utils.find_wine_binary_files(config.TARGET_RELEASE_VERSION)
)
if config.DIALOG == 'cli':
app.input_q.put((
f"Which Wine AppImage or binary should the script use to install {config.FLPRODUCT} v{config.TARGET_RELEASE_VERSION} in {config.INSTALLDIR}?: ", options))
app.event.set()
app.event.wait()
app.event.clear()
config.WINE_EXE = utils.get_relative_path(utils.get_config_var(app.choice_q.get()), config.INSTALLDIR)
else:
utils.send_task(app, 'WINE_EXE')
if config.DIALOG == 'curses':
app.wine_e.wait()
config.WINE_EXE = app.wines_q.get()
else:
if config.DIALOG == 'curses':
app.set_wine(utils.get_wine_exe_path())
Expand Down Expand Up @@ -177,12 +199,28 @@ def ensure_winetricks_choice(app=None):
update_install_feedback("Choose winetricks binary…", app=app)
logging.debug('- config.WINETRICKSBIN')

if app:
if config.WINETRICKSBIN is None:
utils.send_task(app, 'WINETRICKSBIN')
if config.DIALOG == 'curses':
app.tricksbin_e.wait()
config.WINETRICKSBIN = app.tricksbin_q.get()
if config.WINETRICKSBIN is None:
# Check if local winetricks version available; else, download it.
config.WINETRICKSBIN = f"{config.APPDIR_BINDIR}/winetricks"

winetricks_options = utils.get_winetricks_options()

if app:
if config.DIALOG == 'cli':
app.input_q.put((f"Should the script use the system's local winetricks or download the latest winetricks from the Internet? The script needs to set some Wine options that {config.FLPRODUCT} requires on Linux.", winetricks_options))
app.event.wait()
app.event.clear()
winetricksbin = app.choice_q.get()
else:
utils.send_task(app, 'WINETRICKSBIN')
if config.DIALOG == 'curses':
app.tricksbin_e.wait()
winetricksbin = app.tricksbin_q.get()

if not winetricksbin.startswith('Download'):
config.WINETRICKSBIN = winetricksbin
else:
config.WINETRICKSBIN = winetricks_options[0]
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
Expand Down Expand Up @@ -241,7 +279,10 @@ def ensure_installation_config(app=None):
logging.debug(f"> {config.LOGOS64_URL=}")

if app:
utils.send_task(app, 'INSTALL')
if config.DIALOG == 'cli':
msg.logos_msg("Install is running…")
else:
utils.send_task(app, 'INSTALL')


def ensure_install_dirs(app=None):
Expand Down Expand Up @@ -274,10 +315,10 @@ def ensure_install_dirs(app=None):
logging.debug(f"> {config.WINEPREFIX=}")

if app:
utils.send_task(app, 'INSTALLING')
else:
m = f"{utils.get_calling_function_name()}: --install-app is broken"
logging.critical(m)
if config.DIALOG == 'cli':
pass
else:
utils.send_task(app, 'INSTALLING')


def ensure_sys_deps(app=None):
Expand Down Expand Up @@ -608,18 +649,24 @@ def ensure_config_file(app=None):

if different:
if app:
utils.send_task(app, 'CONFIG')
if config.DIALOG == 'curses':
app.config_e.wait()
elif msg.logos_acknowledge_question(
f"Update config file at {config.CONFIG_FILE}?",
"The existing config file was not overwritten."
):
logging.info("Updating config file.")
utils.write_config(config.CONFIG_FILE)
if config.DIALOG == 'cli':
if msg.logos_acknowledge_question(
f"Update config file at {config.CONFIG_FILE}?",
"The existing config file was not overwritten.",
""
):
logging.info("Updating config file.")
utils.write_config(config.CONFIG_FILE)
else:
utils.send_task(app, 'CONFIG')
if config.DIALOG == 'curses':
app.config_e.wait()

if app:
utils.send_task(app, 'DONE')
if config.DIALOG == 'cli':
msg.logos_msg("Install has finished.")
else:
utils.send_task(app, 'DONE')

logging.debug(f"> File exists?: {config.CONFIG_FILE}: {Path(config.CONFIG_FILE).is_file()}") # noqa: E501

Expand Down Expand Up @@ -697,12 +744,15 @@ def ensure_launcher_shortcuts(app=None):
fpath = Path.home() / '.local' / 'share' / 'applications' / f
logging.debug(f"> File exists?: {fpath}: {fpath.is_file()}")
else:
logging.debug("Running from source. Skipping launcher creation.")
update_install_feedback(
"Running from source. Skipping launcher creation.",
app=app
)

if app:
if config.DIALOG == 'cli':
app.stop()


def update_install_feedback(text, app=None):
percent = get_progress_pct(config.INSTALL_STEP, config.INSTALL_STEPS_COUNT)
Expand Down
8 changes: 7 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env python3
import argparse

import cli
import config
import control
import curses
Expand Down Expand Up @@ -239,7 +241,7 @@ def parse_args(args, parser):

# Set ACTION function.
actions = {
'install_app': installer.ensure_launcher_shortcuts,
'install_app': run_cli,
'run_installed_app': logos.LogosManager().start,
'run_indexing': logos.LogosManager().index,
'remove_library_catalog': control.remove_library_catalog,
Expand Down Expand Up @@ -285,6 +287,10 @@ def parse_args(args, parser):
logging.debug(f"{config.ACTION=}")


def run_cli():
cli.command_line_interface()


def run_control_panel():
logging.info(f"Using DIALOG: {config.DIALOG}")
if config.DIALOG is None or config.DIALOG == 'tk':
Expand Down
10 changes: 5 additions & 5 deletions msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ def gui_continue_question(question_text, no_text, secondary):
logos_error(no_text)


def cli_acknowledge_question(QUESTION_TEXT, NO_TEXT):
if not cli_question(QUESTION_TEXT):
logos_msg(NO_TEXT)
def cli_acknowledge_question(question_text, no_text, secondary):
if not cli_question(question_text, secondary):
logos_msg(no_text)
return False
else:
return True
Expand Down Expand Up @@ -264,11 +264,11 @@ def logos_continue_question(question_text, no_text, secondary, app=None):
logos_error(f"Unhandled question: {question_text}")


def logos_acknowledge_question(question_text, no_text):
def logos_acknowledge_question(question_text, no_text, secondary):
if config.DIALOG == 'curses':
pass
else:
return cli_acknowledge_question(question_text, no_text)
return cli_acknowledge_question(question_text, no_text, secondary)


def get_progress_str(percent):
Expand Down
6 changes: 5 additions & 1 deletion network.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,11 +546,15 @@ def get_logos_releases(app=None):
filtered_releases = releases

if app:
app.releases_q.put(filtered_releases)
if config.DIALOG == 'tk':
app.releases_q.put(filtered_releases)
app.root.event_generate(app.release_evt)
elif config.DIALOG == 'curses':
app.releases_q.put(filtered_releases)
app.releases_e.set()
elif config.DIALOG == 'cli':
app.input_q.put((f"Which version of {config.FLPRODUCT} {config.TARGETVERSION} do you want to install?: ", filtered_releases))
app.event.set()
return filtered_releases


Expand Down
2 changes: 2 additions & 0 deletions system.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,8 @@ def install_dependencies(packages, bad_packages, logos9_packages=None, app=None)
app.manualinstall_e.wait()

if not install_deps_failed and not manual_install_required:
if config.DIALOG == 'cli':
command_str = command_str.replace("pkexec", "sudo")
try:
logging.debug(f"Attempting to run this command: {command_str}")
run_command(command_str, shell=True)
Expand Down
Loading