From c2952787c538548ab16056ee22731354c92d06eb Mon Sep 17 00:00:00 2001 From: Laurent LAPORTE Date: Tue, 11 Jun 2024 15:57:59 +0200 Subject: [PATCH] refactor: improved implementation of shortcuts creation --- src/antares_web_installer/app.py | 171 +++++++++++++++++-------------- 1 file changed, 94 insertions(+), 77 deletions(-) diff --git a/src/antares_web_installer/app.py b/src/antares_web_installer/app.py index 89e6937..af4a996 100644 --- a/src/antares_web_installer/app.py +++ b/src/antares_web_installer/app.py @@ -5,16 +5,15 @@ import textwrap import time import webbrowser -import psutil - from pathlib import Path -from importlib import resources from shutil import copy2, copytree +import psutil + from antares_web_installer.config import update_config +from antares_web_installer.shortcuts import create_shortcut, get_desktop -if os.name == 'nt': - from antares_web_installer.shortcuts import _win32_shell +logger = logging.getLogger(__name__) # List of files and directories to exclude during installation COMMON_EXCLUDED_FILES = {"config.prod.yaml", "config.yaml", "examples", "logs", "matrices", "tmp"} @@ -22,6 +21,9 @@ WINDOWS_EXCLUDED_FILES = COMMON_EXCLUDED_FILES | {"AntaresWebWorker.exe"} EXCLUDED_FILES = POSIX_EXCLUDED_FILES if os.name == "posix" else WINDOWS_EXCLUDED_FILES +SERVER_NAMES = {"posix": "AntaresWebServer", "nt": "AntaresWebServer.exe"} +SHORTCUT_NAMES = {"posix": "AntaresWebServer.desktop", "nt": "AntaresWebServer.lnk"} + class InstallError(Exception): """ @@ -39,19 +41,18 @@ class App: launch: bool = False browser: bool = False - logger = logging.getLogger(__name__) - target_file: Path = dataclasses.field(init=False) + server_path: Path = dataclasses.field(init=False) def __post_init__(self): - self.target_file = self.target_dir.joinpath("AntaresWeb/AntaresWebServer") - if self.os_name == "nt": - self.target_file = self.target_file.with_suffix(".exe") + # Prepare the path to the executable which is located in the target directory + server_name = SERVER_NAMES[self.os_name] + self.server_path = self.target_dir.joinpath("AntaresWeb", server_name) def run(self) -> None: self.kill_running_server() self.install_files() if self.shortcut: - self.create_icons() + self.create_shortcuts() if self.launch: self.start_server() if self.browser: @@ -62,30 +63,31 @@ def kill_running_server(self) -> None: Check whether Antares service is up. Kill the process if so. """ + server_name = SERVER_NAMES[self.os_name] for proc in psutil.process_iter(["pid", "name"]): - if "antareswebserver" in proc.name().lower(): - self.logger.info("Running server found. We will attempt to stop it.") + if server_name.lower() in proc.name().lower(): + logger.info("Running server found. We will attempt to stop it.") running_app = psutil.Process(pid=proc.pid) running_app.kill() running_app.wait(15) - self.logger.info("The application was successfully stopped.") - self.logger.info("No other processes found.") + logger.info("The application was successfully stopped.") + + logger.info("No other processes found.") def install_files(self): - """ - """ + """ """ # if the target directory already exists and isn't empty if self.target_dir.is_dir() and list(self.target_dir.iterdir()): # check app version version = self.check_version() - self.logger.info(f"Old application version : {version}.") + logger.info(f"Old application version : {version}.") # update config file config_path = self.target_dir.joinpath("config.yaml") update_config(config_path, config_path, version) - self.logger.info(f"New application version : {version}.") + logger.info(f"New application version : {version}.") # copy binaries self.copy_files() @@ -105,11 +107,11 @@ def copy_files(self): if elt_path.name not in EXCLUDED_FILES: try: if elt_path.is_file(): - self.logger.info(f"'{elt_path}' file found and isn't an excluded file.") + logger.info(f"'{elt_path}' file found and isn't an excluded file.") copy2(elt_path, self.target_dir) else: # copy new directory - self.logger.info(f"'{elt_path}' directory found and isn't an excluded directory.") + logger.info(f"'{elt_path}' directory found and isn't an excluded directory.") copytree(elt_path, self.target_dir.joinpath(elt_path.name), dirs_exist_ok=True) # handle permission errors @@ -121,12 +123,10 @@ def check_version(self) -> str: Execute command to get the current version of the server. """ # check user's os - if self.os_name.lower() == "posix": # if os is linux, remove ".exe" - exe_path = self.target_file.with_suffix("") - args = [str(self.target_file), "--version"] + args = [str(self.server_path), "--version"] try: - self.logger.info(f"Attempt to get version of Antares server...") + logger.info("Attempt to get version of Antares server...") version = subprocess.check_output(args, text=True, stderr=subprocess.PIPE).strip() except FileNotFoundError as e: raise InstallError(f"Can't check version: {e}") from e @@ -134,72 +134,89 @@ def check_version(self) -> str: reason = textwrap.indent(e.stderr, " | ", predicate=lambda line: True) raise InstallError(f"Can't check version:\n{reason}") from e - self.logger.info(f"Version found.") + logger.info("Version found.") return version - def create_icons(self): + def create_shortcuts(self): """ Create a local server icon and a browser icon on desktop and """ # using pyshortcuts - self.logger.info("Generating server shortcut ...") - - # if user's os is linux - if self.os_name.lower() == "posix": - self.logger.info("Unix os detected.") - - # 1. create a .desktop - desktop_path = os.popen("xdg-user-dir DESKTOP").read().rstrip('\n') - shortcut_name = "AntaresWebServer.desktop" - shortcut_path = desktop_path + '/' + shortcut_name - os.popen(f"touch {shortcut_path}") - - # 2. write the default desktop entry - with resources.path( - "antares_web_installer.assets.img", - "antares-web-installer-logo.png") as icon_path: # deprecated since 3.11 version - with open(shortcut_path, mode="w") as file: - content = (f"[Desktop Entry]\n" - f"Version=1.0\n" - f"Type=Application\n" - f"Terminal=true\n" - f"Exec={str(self.target_file.resolve().expanduser())}\n" - f"Name=Antares Web Server\n" - f"Comment=Launch Antares web server\n" - f"Icon={str(icon_path.resolve().expanduser())}") - - file.write(content) - - # 4. activate allow launching option - os.popen(f"chmod u+x {shortcut_path}") - self.logger.info("Execution rights were updated") - - os.popen(f"gio set {shortcut_path} metadata::trusted true") - self.logger.info("Shortcut is now allowed to launch the server.") - - # 5. Option add to application list - # os.popen(f"") - - # 6. Option : add to path - # alias_command = f"alias AntaresWebServer=$PATH:{self.target_dir}\nalias antareswebserver=AntaresWebServer\n" - # os.popen(f"echo '{alias_command}' >> {shortcut_path}") - - # otherwise, consider user's os is windows - else: - _win32_shell.create_shortcut(self.target_dir, self.target_file) + logger.info("Generating server shortcut...") + + # prepare a shortcut into the desktop directory + shortcut_name = SHORTCUT_NAMES[self.os_name] + shortcut_path = Path(get_desktop()).joinpath(shortcut_name) + + # if the shortcut already exists, remove it + shortcut_path.unlink(missing_ok=True) + + # create a new shortcut + create_shortcut( + shortcut_path, + exe_path=self.server_path, + working_dir=self.target_dir, + description="Launch Antares Web Server in background", + ) + + # # if user's os is linux + # if self.os_name.lower() == "posix": + # logger.info("Unix os detected.") + # + # # 1. create a .desktop + # desktop_path = os.popen("xdg-user-dir DESKTOP").read().rstrip("\n") + # shortcut_name = "AntaresWebServer.desktop" + # shortcut_path = desktop_path + "/" + shortcut_name + # os.popen(f"touch {shortcut_path}") + # + # # 2. write the default desktop entry + # with resources.path( + # "antares_web_installer.assets.img", "antares-web-installer-logo.png" + # ) as icon_path: # deprecated since 3.11 version + # with open(shortcut_path, mode="w") as file: + # content = ( + # f"[Desktop Entry]\n" + # f"Version=1.0\n" + # f"Type=Application\n" + # f"Terminal=true\n" + # f"Exec={str(self.target_file.resolve().expanduser())}\n" + # f"Name=Antares Web Server\n" + # f"Comment=Launch Antares web server\n" + # f"Icon={str(icon_path.resolve().expanduser())}" + # ) + # + # file.write(content) + # + # # 4. activate allow launching option + # os.popen(f"chmod u+x {shortcut_path}") + # logger.info("Execution rights were updated") + # + # os.popen(f"gio set {shortcut_path} metadata::trusted true") + # logger.info("Shortcut is now allowed to launch the server.") + # + # # 5. Option add to application list + # # os.popen(f"") + # + # # 6. Option : add to path + # # alias_command = f"alias AntaresWebServer=$PATH:{self.target_dir}\nalias antareswebserver=AntaresWebServer\n" + # # os.popen(f"echo '{alias_command}' >> {shortcut_path}") + # + # # otherwise, consider user's os is windows + # else: + # _win32_shell.create_shortcut(self.target_dir, self.target_file) # test if it already exists - self.logger.info("Server shortcut was created.") + logger.info("Server shortcut was created.") def start_server(self): """ Launch the local server as a background task """ - args = [f"{self.target_file}"] + args = [str(self.server_path)] server_process = subprocess.Popen(args=args, shell=True, start_new_session=True) time.sleep(1.5) # wait for the server to complete startup if server_process.poll() is None: - self.logger.info(f"Server was started successfully.") + logger.info("Server was started successfully.") def open_browser(self): """ @@ -209,6 +226,6 @@ def open_browser(self): try: webbrowser.open(url=url, new=2) except webbrowser.Error as e: - raise InstallError(f"Could not open browser at '{url}': {e}") + raise InstallError(f"Could not open browser at '{url}': {e}") from e else: - self.logger.info(f"Browser was successfully opened at '{url}'.") + logger.info(f"Browser was successfully opened at '{url}'.")