Loading Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ macos:locales ## construire le paquet pour MacOS tar -cvzf dist/climax-macos_arm.tar.gz -C dist/climax . .PHONY:macos macosdistro:macos ## distribuer la version pour macos scp dist/climax-macos_arm.tar.gz climaxdistro:/var/www/climax/climax-macos_arm.tar.gz .PHONY:macosdistro macosinstall:macos ## installer le paquet macos localement mkdir -p "${HOME}/.climax/" cp -r dist/climax/* "${HOME}/.climax/" Loading pyproject.toml +4 −2 Original line number Diff line number Diff line [project] name = "climax-CERTIC" version = "0.2.0" dynamic = ["version"] description = "CLI tool for MaX, Le Moteur d'Affichage XML." readme = "README.md" license = {text = "CECILL-C"} Loading Loading @@ -32,7 +32,7 @@ dev = [ ] [project.scripts] climax = 'climax.__main__:app' climax = 'climax.__main__:wrap_app_exceptions' [build-system] requires = ["hatchling"] Loading @@ -46,3 +46,5 @@ packages = ["src/climax"] [tool.hatch.metadata] allow-direct-references = true [tool.hatch.version] path = "src/climax/__init__.py" No newline at end of file src/climax.py +2 −2 Original line number Diff line number Diff line from climax.__main__ import app from climax.__main__ import wrap_app_exceptions if __name__ == "__main__": app() wrap_app_exceptions() src/climax/__init__.py +1 −0 Original line number Diff line number Diff line __version__ = "0.2.1" src/climax/__main__.py +77 −20 Original line number Diff line number Diff line import subprocess from pathlib import Path from typing import Optional, Union, Iterator, List from typing import Optional, Union, Iterator, List, Any from typing_extensions import Annotated import gettext import tempfile Loading Loading @@ -31,6 +31,7 @@ from .config import ( max_release, latest_max_release, CLI_MAIN_HELP, CRASH_MESSAGE, ) locales_dir = Path(__file__).parent / "locales" Loading @@ -40,13 +41,21 @@ _ = gettext.gettext import typer # noqa: E402 from rich import print # noqa: E402 from rich import print as rich_print # noqa: E402 from rich.progress import track # noqa: E402 from rich.console import Console # noqa: E402 from rich.table import Table # noqa: E402 climax_db = db() last_project_dir = None def _track_last_dir(directory: Any) -> Any: global last_project_dir last_project_dir = directory return directory def _port_is_in_use(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: Loading Loading @@ -127,13 +136,14 @@ def dir_is_max(directory: Path = os.getcwd()) -> bool: if conf: ns = conf.get("xmlns") if ns == "http://certic.unicaen.fr/max/ns/1.0": _track_last_dir(directory) return True return False def check_dir_is_max(directory): if not dir_is_max(directory): print( rich_print( "[red]{}[/red]".format(_("Le dossier n'est pas une installation de MaX.")) ) raise typer.Exit(code=1) Loading @@ -158,7 +168,9 @@ def cached_download( else: response = requests.get(source, stream=True) if response.status_code != 200: print("[red]{}[/red] {}".format(_("Impossible de télécharger"), source)) rich_print( "[red]{}[/red] {}".format(_("Impossible de télécharger"), source) ) raise typer.Exit(code=1) try: total_size = int(response.headers["Content-length"]) Loading Loading @@ -203,7 +215,7 @@ def ensure_java() -> Path: "java", ) if not java_bin: print( rich_print( "[red]" + _("Java est requis pour utiliser MaX: https://openjdk.org/install/") + "[/red]" Loading @@ -220,7 +232,7 @@ def ensure_available_max_directory(directory: str = os.getcwd()) -> tuple[Path, is_empty = not any(directory.iterdir()) is_max = dir_is_max(directory) if not is_empty and not is_max: print( rich_print( "[red]{}[/red]".format( _( "Le dossier n'est pas vide et n'est pas un projet MaX valide, veuillez en choisir un autre." Loading Loading @@ -294,7 +306,7 @@ def _copy_max_tree( def _install_new_max_instance(root_directory: Path, init_values: dict = None): print( rich_print( "[bold]" + _("Initialisation d'une nouvelle instance de MaX dans {}").format( root_directory Loading Loading @@ -332,7 +344,7 @@ def _install_new_max_instance(root_directory: Path, init_values: dict = None): f.write(WELCOME_PAGE) climax_db.save(MaxInstall(None, str(root_directory.resolve()))) climax_db.commit() print( rich_print( "[bold]" + _("La nouvelle instance de MaX est prête dans {}").format(root_directory) + "[/bold]" Loading @@ -346,7 +358,7 @@ def _install_new_max_instance(root_directory: Path, init_values: dict = None): def _sync_max_instance(root_directory: Path, verbose=True): if verbose: print( rich_print( "[bold]" + _("Initialisation de l'instance de MaX existant dans {}").format( root_directory Loading Loading @@ -379,7 +391,7 @@ def _sync_max_instance(root_directory: Path, verbose=True): _sync_bundles(root_directory) if verbose: print( rich_print( "[bold]" + _("L'instance de MaX est prête dans {}").format(root_directory) + "[/bold]" Loading Loading @@ -455,8 +467,10 @@ def demo( shutil.rmtree(Path(cur_dir, "max"), ignore_errors=True) _sync_max_instance(cur_dir, verbose=False) print("[green]" + _("édition de démonstration installée") + "[/green]") print(_("Vous pouvez démarrer MaX avec la commande [bold]climax start[/bold]")) rich_print("[green]" + _("édition de démonstration installée") + "[/green]") rich_print( _("Vous pouvez démarrer MaX avec la commande [bold]climax start[/bold]") ) @app.command( Loading Loading @@ -502,7 +516,7 @@ def start( ] if service: process_args.append("-S") print( rich_print( "[green]" + _("Démarrage de MaX sur http://{}:{}").format(http_host, http_port) + "[/green]" Loading Loading @@ -599,7 +613,7 @@ def freeze( raise e # stop server stop(stop_port, directory) print("[green]" + _("Site copié dans {}").format(str(output)) + "[/green]") rich_print("[green]" + _("Site copié dans {}").format(str(output)) + "[/green]") @app.command(help=_("Liste les bundles disponibles")) Loading Loading @@ -656,7 +670,7 @@ def bundles_add( if type(from_archive) is str: from_archive = Path(from_archive) if not from_archive.is_file(): print( rich_print( _("[red][bold]{}[/bold] n'est pas un chemin valide[/red]").format( from_archive ) Loading @@ -664,7 +678,7 @@ def bundles_add( raise typer.Exit(code=1) fname, extension = os.path.splitext(from_archive) if extension != ".zip": print(_("[red]{} n'est pas un bundle[/red]").format(from_archive)) rich_print(_("[red]{} n'est pas un bundle[/red]").format(from_archive)) raise typer.Exit(code=1) local_destination = Path( directory, Loading @@ -685,7 +699,9 @@ def bundles_add( bundles_list(directory) else: if bundle_name not in config.available_bundles.keys(): print(_("[red]{} n'est pas un bundle disponible[/red]").format(bundle_name)) rich_print( _("[red]{} n'est pas un bundle disponible[/red]").format(bundle_name) ) raise typer.Exit(code=1) else: for ( Loading Loading @@ -718,7 +734,7 @@ def bundles_remove( keep_bundles = {} bundle_name = bundle_name.lower().strip() if bundle_name not in config.bundles.keys(): print(_("[red]{} n'est pas un bundle actif[/red]").format(bundle_name)) rich_print(_("[red]{} n'est pas un bundle actif[/red]").format(bundle_name)) raise typer.Exit(code=1) for active_bundle_name, active_bundle_url in config.bundles.items(): if active_bundle_name.strip() != bundle_name.strip(): Loading @@ -740,7 +756,7 @@ def feed( java_bin_path = ensure_java() feed_path = Path(feed_path).resolve() if not feed_path.is_file() and not feed_path.is_dir(): print(_("[red]{} n'existe pas[/red]").format(feed_path)) rich_print(_("[red]{} n'existe pas[/red]").format(feed_path)) raise typer.Exit(code=1) process_args = [ str(java_bin_path), Loading Loading @@ -905,6 +921,18 @@ def cache_clear(): shutil.rmtree(Path(USER_MAX_DIR, "max"), ignore_errors=True) if os.environ.get("CLIMAX_DEBUG") == "True": @app.command(help=_("Planter Climax")) def crash( directory: Annotated[ str, typer.Option(help=_("Dossier du projet MaX")) ] = os.getcwd(), ): dir_is_max(directory) raise ValueError("Something wrong happened.") # @app.command(help=_("Application web (test)")) # def ui(max_dir: str = None): # from .gui import app Loading @@ -915,5 +943,34 @@ def cache_clear(): get_yolk() if __name__ == "__main__": def wrap_app_exceptions(): try: app() except Exception: import traceback import datetime import platform import json import sys from . import __version__ as climax_version global last_project_dir max_config = "" if last_project_dir is not None: with open(Path(last_project_dir, "config.xml"), "r") as f: max_config = f.read() bug_report = { "time": datetime.datetime.now(datetime.UTC).timestamp(), "platform": platform.platform(), "climax_version": climax_version, "max_config": max_config, "working_dir": os.getcwd(), "traceback": traceback.format_exc(), } sys.exit(CRASH_MESSAGE.format(json.dumps(bug_report, indent=2))) if __name__ == "__main__": wrap_app_exceptions() Loading
Makefile +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,10 @@ macos:locales ## construire le paquet pour MacOS tar -cvzf dist/climax-macos_arm.tar.gz -C dist/climax . .PHONY:macos macosdistro:macos ## distribuer la version pour macos scp dist/climax-macos_arm.tar.gz climaxdistro:/var/www/climax/climax-macos_arm.tar.gz .PHONY:macosdistro macosinstall:macos ## installer le paquet macos localement mkdir -p "${HOME}/.climax/" cp -r dist/climax/* "${HOME}/.climax/" Loading
pyproject.toml +4 −2 Original line number Diff line number Diff line [project] name = "climax-CERTIC" version = "0.2.0" dynamic = ["version"] description = "CLI tool for MaX, Le Moteur d'Affichage XML." readme = "README.md" license = {text = "CECILL-C"} Loading Loading @@ -32,7 +32,7 @@ dev = [ ] [project.scripts] climax = 'climax.__main__:app' climax = 'climax.__main__:wrap_app_exceptions' [build-system] requires = ["hatchling"] Loading @@ -46,3 +46,5 @@ packages = ["src/climax"] [tool.hatch.metadata] allow-direct-references = true [tool.hatch.version] path = "src/climax/__init__.py" No newline at end of file
src/climax.py +2 −2 Original line number Diff line number Diff line from climax.__main__ import app from climax.__main__ import wrap_app_exceptions if __name__ == "__main__": app() wrap_app_exceptions()
src/climax/__main__.py +77 −20 Original line number Diff line number Diff line import subprocess from pathlib import Path from typing import Optional, Union, Iterator, List from typing import Optional, Union, Iterator, List, Any from typing_extensions import Annotated import gettext import tempfile Loading Loading @@ -31,6 +31,7 @@ from .config import ( max_release, latest_max_release, CLI_MAIN_HELP, CRASH_MESSAGE, ) locales_dir = Path(__file__).parent / "locales" Loading @@ -40,13 +41,21 @@ _ = gettext.gettext import typer # noqa: E402 from rich import print # noqa: E402 from rich import print as rich_print # noqa: E402 from rich.progress import track # noqa: E402 from rich.console import Console # noqa: E402 from rich.table import Table # noqa: E402 climax_db = db() last_project_dir = None def _track_last_dir(directory: Any) -> Any: global last_project_dir last_project_dir = directory return directory def _port_is_in_use(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: Loading Loading @@ -127,13 +136,14 @@ def dir_is_max(directory: Path = os.getcwd()) -> bool: if conf: ns = conf.get("xmlns") if ns == "http://certic.unicaen.fr/max/ns/1.0": _track_last_dir(directory) return True return False def check_dir_is_max(directory): if not dir_is_max(directory): print( rich_print( "[red]{}[/red]".format(_("Le dossier n'est pas une installation de MaX.")) ) raise typer.Exit(code=1) Loading @@ -158,7 +168,9 @@ def cached_download( else: response = requests.get(source, stream=True) if response.status_code != 200: print("[red]{}[/red] {}".format(_("Impossible de télécharger"), source)) rich_print( "[red]{}[/red] {}".format(_("Impossible de télécharger"), source) ) raise typer.Exit(code=1) try: total_size = int(response.headers["Content-length"]) Loading Loading @@ -203,7 +215,7 @@ def ensure_java() -> Path: "java", ) if not java_bin: print( rich_print( "[red]" + _("Java est requis pour utiliser MaX: https://openjdk.org/install/") + "[/red]" Loading @@ -220,7 +232,7 @@ def ensure_available_max_directory(directory: str = os.getcwd()) -> tuple[Path, is_empty = not any(directory.iterdir()) is_max = dir_is_max(directory) if not is_empty and not is_max: print( rich_print( "[red]{}[/red]".format( _( "Le dossier n'est pas vide et n'est pas un projet MaX valide, veuillez en choisir un autre." Loading Loading @@ -294,7 +306,7 @@ def _copy_max_tree( def _install_new_max_instance(root_directory: Path, init_values: dict = None): print( rich_print( "[bold]" + _("Initialisation d'une nouvelle instance de MaX dans {}").format( root_directory Loading Loading @@ -332,7 +344,7 @@ def _install_new_max_instance(root_directory: Path, init_values: dict = None): f.write(WELCOME_PAGE) climax_db.save(MaxInstall(None, str(root_directory.resolve()))) climax_db.commit() print( rich_print( "[bold]" + _("La nouvelle instance de MaX est prête dans {}").format(root_directory) + "[/bold]" Loading @@ -346,7 +358,7 @@ def _install_new_max_instance(root_directory: Path, init_values: dict = None): def _sync_max_instance(root_directory: Path, verbose=True): if verbose: print( rich_print( "[bold]" + _("Initialisation de l'instance de MaX existant dans {}").format( root_directory Loading Loading @@ -379,7 +391,7 @@ def _sync_max_instance(root_directory: Path, verbose=True): _sync_bundles(root_directory) if verbose: print( rich_print( "[bold]" + _("L'instance de MaX est prête dans {}").format(root_directory) + "[/bold]" Loading Loading @@ -455,8 +467,10 @@ def demo( shutil.rmtree(Path(cur_dir, "max"), ignore_errors=True) _sync_max_instance(cur_dir, verbose=False) print("[green]" + _("édition de démonstration installée") + "[/green]") print(_("Vous pouvez démarrer MaX avec la commande [bold]climax start[/bold]")) rich_print("[green]" + _("édition de démonstration installée") + "[/green]") rich_print( _("Vous pouvez démarrer MaX avec la commande [bold]climax start[/bold]") ) @app.command( Loading Loading @@ -502,7 +516,7 @@ def start( ] if service: process_args.append("-S") print( rich_print( "[green]" + _("Démarrage de MaX sur http://{}:{}").format(http_host, http_port) + "[/green]" Loading Loading @@ -599,7 +613,7 @@ def freeze( raise e # stop server stop(stop_port, directory) print("[green]" + _("Site copié dans {}").format(str(output)) + "[/green]") rich_print("[green]" + _("Site copié dans {}").format(str(output)) + "[/green]") @app.command(help=_("Liste les bundles disponibles")) Loading Loading @@ -656,7 +670,7 @@ def bundles_add( if type(from_archive) is str: from_archive = Path(from_archive) if not from_archive.is_file(): print( rich_print( _("[red][bold]{}[/bold] n'est pas un chemin valide[/red]").format( from_archive ) Loading @@ -664,7 +678,7 @@ def bundles_add( raise typer.Exit(code=1) fname, extension = os.path.splitext(from_archive) if extension != ".zip": print(_("[red]{} n'est pas un bundle[/red]").format(from_archive)) rich_print(_("[red]{} n'est pas un bundle[/red]").format(from_archive)) raise typer.Exit(code=1) local_destination = Path( directory, Loading @@ -685,7 +699,9 @@ def bundles_add( bundles_list(directory) else: if bundle_name not in config.available_bundles.keys(): print(_("[red]{} n'est pas un bundle disponible[/red]").format(bundle_name)) rich_print( _("[red]{} n'est pas un bundle disponible[/red]").format(bundle_name) ) raise typer.Exit(code=1) else: for ( Loading Loading @@ -718,7 +734,7 @@ def bundles_remove( keep_bundles = {} bundle_name = bundle_name.lower().strip() if bundle_name not in config.bundles.keys(): print(_("[red]{} n'est pas un bundle actif[/red]").format(bundle_name)) rich_print(_("[red]{} n'est pas un bundle actif[/red]").format(bundle_name)) raise typer.Exit(code=1) for active_bundle_name, active_bundle_url in config.bundles.items(): if active_bundle_name.strip() != bundle_name.strip(): Loading @@ -740,7 +756,7 @@ def feed( java_bin_path = ensure_java() feed_path = Path(feed_path).resolve() if not feed_path.is_file() and not feed_path.is_dir(): print(_("[red]{} n'existe pas[/red]").format(feed_path)) rich_print(_("[red]{} n'existe pas[/red]").format(feed_path)) raise typer.Exit(code=1) process_args = [ str(java_bin_path), Loading Loading @@ -905,6 +921,18 @@ def cache_clear(): shutil.rmtree(Path(USER_MAX_DIR, "max"), ignore_errors=True) if os.environ.get("CLIMAX_DEBUG") == "True": @app.command(help=_("Planter Climax")) def crash( directory: Annotated[ str, typer.Option(help=_("Dossier du projet MaX")) ] = os.getcwd(), ): dir_is_max(directory) raise ValueError("Something wrong happened.") # @app.command(help=_("Application web (test)")) # def ui(max_dir: str = None): # from .gui import app Loading @@ -915,5 +943,34 @@ def cache_clear(): get_yolk() if __name__ == "__main__": def wrap_app_exceptions(): try: app() except Exception: import traceback import datetime import platform import json import sys from . import __version__ as climax_version global last_project_dir max_config = "" if last_project_dir is not None: with open(Path(last_project_dir, "config.xml"), "r") as f: max_config = f.read() bug_report = { "time": datetime.datetime.now(datetime.UTC).timestamp(), "platform": platform.platform(), "climax_version": climax_version, "max_config": max_config, "working_dir": os.getcwd(), "traceback": traceback.format_exc(), } sys.exit(CRASH_MESSAGE.format(json.dumps(bug_report, indent=2))) if __name__ == "__main__": wrap_app_exceptions()