Commit ba3fb67b authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

add directory option to most commands

parent 6a315541
Loading
Loading
Loading
Loading
+196 −140
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ from .config import (
    max_releases,
    max_release,
    latest_max_release,
    CLI_MAIN_HELP,
)

locales_dir = Path(__file__).parent / "locales"
@@ -97,8 +98,8 @@ def class_path_separator() -> str:


@cache
def cp_paths() -> str:
    basex_dir_path = Path(os.getcwd(), ".max", "basex")
def cp_paths(working_dir: str = os.getcwd()) -> str:
    basex_dir_path = Path(working_dir, ".max", "basex")
    paths = class_path_separator().join(
        [
            str(Path(basex_dir_path, "BaseX.jar")),
@@ -116,9 +117,7 @@ def find_jdk(cur_sys: str = None) -> Optional[str]:
    return JAVA_DISTROS.get(cur_sys, None)


def dir_is_max(directory: Path = None) -> bool:
    if directory is None:
        directory = os.getcwd()
def dir_is_max(directory: Path = os.getcwd()) -> bool:
    directory = Path(directory)
    config_file = Path(directory, "config.xml")
    if config_file.exists():
@@ -132,16 +131,16 @@ def dir_is_max(directory: Path = None) -> bool:
    return False


def check_cwd_is_max():
    if not dir_is_max():
def check_dir_is_max(directory):
    if not dir_is_max(directory):
        print(
            "[red]{}[/red]".format(_("Le dossier n'est pas une installation de MaX."))
        )
        raise typer.Exit(code=1)


def max_config() -> MaXProjectConfig:
    return MaXProjectConfig(Path(os.getcwd(), MAX_CONFIG_FILE))
def max_config(directory: str = os.getcwd()) -> MaXProjectConfig:
    return MaXProjectConfig(Path(directory, MAX_CONFIG_FILE))


def unzip(source: Union[Path, str], destination: Union[Path, str]) -> bool:
@@ -213,9 +212,7 @@ def ensure_java() -> Path:
    return java_bin


def ensure_available_max_directory(directory: Optional[Path]) -> tuple[Path, bool]:
    if not directory:
        directory = os.getcwd()
def ensure_available_max_directory(directory: str = os.getcwd()) -> tuple[Path, bool]:
    directory = Path(directory)
    if not directory.exists():
        directory.mkdir(parents=True, exist_ok=False)
@@ -234,9 +231,7 @@ def ensure_available_max_directory(directory: Optional[Path]) -> tuple[Path, boo
    return directory, is_max


app = typer.Typer(
    help=_("Utilitaire en ligne de commande pour la gestion des projets MaX")
)
app = typer.Typer(help=_(CLI_MAIN_HELP))


def _sync_bundles(root_directory: Path):
@@ -391,43 +386,11 @@ def _sync_max_instance(root_directory: Path, verbose=True):
        )


@app.command(help=_("Initialisation d'une instance existante de MaX"))
def sync(
    directory: Annotated[
        Optional[Path], typer.Argument(help=_("chemin vers un dossier"))
    ] = os.getcwd(),
):
    ensure_java()
    root_directory, is_max = ensure_available_max_directory(directory)

    if is_max:
        _sync_max_instance(root_directory)
        climax_db.save(MaxInstall(None, str(root_directory.resolve())))
        climax_db.commit()
    else:
        print(
            _(
                "Le dossier ne contient pas une instance de MaX. Utiliser la commande new"
@app.command(
    help=_(
        "Création d'une nouvelle instance de MaX\n\nL'option --interactive propose un menu pour choisir sa configuration."
    )
)
        raise typer.Exit()


def _choose_between(values: List[str], label: str = "") -> str:
    try:
        from simple_term_menu import TerminalMenu

        terminal_menu = TerminalMenu(values, title=label)
        menu_entry_index = terminal_menu.show()
        return values[menu_entry_index]
    except NotImplementedError:  # mainly windows
        choice = None
        while choice not in values:
            choice = typer.prompt("{} ({})".format(_(label), ", ".join(values)))
        return choice


@app.command(help=_("Création d'une nouvelle instance de MaX"))
def new(
    directory: Annotated[
        Optional[Path], typer.Argument(help=_("chemin vers un dossier"))
@@ -457,15 +420,17 @@ def new(
    _install_new_max_instance(root_directory, init_values)


@app.command(
    help=_("Installe une édition de démonstration dans l'instance de MaX en cours")
)
def demo():
    check_cwd_is_max()
@app.command(help=_("Installe une édition de démonstration."))
def demo(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    if typer.confirm(
        _("Voulez-vous installer une édition de démonstration ?"), default=False
    ):
        cur_dir = os.getcwd()
        cur_dir = directory
        config = MaXProjectConfig(Path(cur_dir, "config.xml"))
        with tempfile.TemporaryDirectory() as tmpdirname:
            zip_destination = Path(tmpdirname, "max.zip")
@@ -493,23 +458,11 @@ def demo():
        print(_("Vous pouvez démarrer MaX avec la commande [bold]climax start[/bold]"))


@app.command(help=_("Arrête l'instance de MaX du dossier en cours"))
def stop(http_stop_port: int = STOP_PORT):
    check_cwd_is_max()
    java_bin_path = ensure_java()
    process_args = [
        str(java_bin_path),
        "-cp",
        cp_paths(),
        "-Xmx2g",
        "org.basex.BaseXHTTP",
        f"-s{http_stop_port}",
        "stop",
    ]
    subprocess.run(process_args)


@app.command(help=_("Démarre l'instance de MaX du dossier en cours"))
@app.command(
    help=_(
        "Démarre l'instance de MaX.\n\nSi les ports choisis ne sont pas disponibles, climax tentera de lancer le serveur sur d'autres ports."
    )
)
def start(
    http_host: str = WEB_HOST,
    http_port: int = WEB_PORT,
@@ -523,18 +476,21 @@ def start(
            )
        ),
    ] = False,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    basex_port = _closer_free_port_to(basex_port)
    http_port = _closer_free_port_to(http_port)
    if http_stop_port <= http_port:
        http_stop_port = http_port + 1
    http_stop_port = _closer_free_port_to(http_stop_port)
    check_cwd_is_max()
    check_dir_is_max(directory)
    java_bin_path = ensure_java()
    process_args = [
        str(java_bin_path),
        "-cp",
        cp_paths(),
        cp_paths(directory),
        "-Xmx2g",
        "org.basex.BaseXHTTP",
        f"-p{basex_port}",
@@ -552,18 +508,49 @@ def start(
    subprocess.run(process_args)


@app.command(help=_("Efface le cache de climax"))
def cache_clear():
    for p in CACHE_DIR.iterdir():
        if p.is_file():
            p.unlink(missing_ok=True)
    shutil.rmtree(Path(USER_MAX_DIR, "max"), ignore_errors=True)
def _choose_between(values: List[str], label: str = "") -> str:
    try:
        from simple_term_menu import TerminalMenu

        terminal_menu = TerminalMenu(values, title=label)
        menu_entry_index = terminal_menu.show()
        return values[menu_entry_index]
    except NotImplementedError:  # mainly windows
        choice = None
        while choice not in values:
            choice = typer.prompt("{} ({})".format(_(label), ", ".join(values)))
        return choice


@app.command(help=_("Arrête l'instance de MaX"))
def stop(
    http_stop_port: int = STOP_PORT,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    java_bin_path = ensure_java()
    process_args = [
        str(java_bin_path),
        "-cp",
        cp_paths(directory),
        "-Xmx2g",
        "org.basex.BaseXHTTP",
        f"-s{http_stop_port}",
        "stop",
    ]
    subprocess.run(process_args)


@app.command(help=_("Affiche la configuration de MaX"))
def info():
    check_cwd_is_max()
    config = max_config()
def info(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    config = max_config(directory)
    console = Console()
    table = Table(_("Nom"), _("Valeur"), show_lines=True)
    table.add_row(_("Version de MaX"), config.max_version.get("name"))
@@ -575,19 +562,25 @@ def info():
    console.print(table)


@app.command(help=_("Fait une copie HTML statique du projet dans le dossier"))
@app.command(help=_("Fait une copie HTML statique"))
def freeze(
    directory: Annotated[
        Optional[Path], typer.Argument(help=_("chemin vers un dossier"))
    ] = Path(os.getcwd(), "output"),
    debug: bool = False,
):
    max_config()
    max_config(directory)
    # start server on specific port
    port_number = _free_port()
    stop_port = port_number + 1
    stop_port = _closer_free_port_to(stop_port)
    start(WEB_HOST, port_number, http_stop_port=stop_port, service=True)
    start(
        WEB_HOST,
        port_number,
        http_stop_port=stop_port,
        service=True,
        directory=directory,
    )
    # copy website
    try:
        start_url = f"http://localhost:{port_number}/"
@@ -596,31 +589,18 @@ def freeze(
        stop(stop_port)
        raise e
    # stop server
    stop(stop_port)
    stop(stop_port, directory)
    print("[green]" + _("Site copié dans {}").format(str(directory)) + "[/green]")


@app.command(help=_("Supprime un bundle pour l'instance de Max en cours"))
def bundles_remove(bundle_name: str):
    check_cwd_is_max()
    config = max_config()
    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))
        raise typer.Exit(code=1)
    for active_bundle_name, active_bundle_url in config.bundles.items():
        if active_bundle_name.strip() != bundle_name.strip():
            keep_bundles[active_bundle_name] = active_bundle_url
    config.bundles = keep_bundles
    config.write()
    bundles_list()


@app.command(help=_("Liste les bundles disponibles"))
def bundles_list():
    check_cwd_is_max()
    config = max_config()
def bundles_list(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    config = max_config(directory)
    console = Console()
    table = Table(_("Nom"), _("Installé"), _("Description"), show_lines=True)
    bundles_done = {}
@@ -646,11 +626,16 @@ def bundles_list():
    console.print(table)


@app.command(help=_("Ajoute un bundle pour l'instance de Max en cours"))
def bundles_add(bundle_name: str):
@app.command(help=_("Ajoute un bundle"))
def bundles_add(
    bundle_name: str,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    from_archive = None
    check_cwd_is_max()
    config = max_config()
    check_dir_is_max(directory)
    config = max_config(directory)
    current_bundles_config = config.bundles
    # bundle_name is a local archive
    if bundle_name.endswith(".zip") and Path(bundle_name).is_file():
@@ -671,7 +656,7 @@ def bundles_add(bundle_name: str):
            print(_("[red]{} n'est pas un bundle[/red]").format(from_archive))
            raise typer.Exit(code=1)
        local_destination = Path(
            os.getcwd(),
            directory,
            ".max",
            "resources",
            "local_bundles",
@@ -685,8 +670,8 @@ def bundles_add(bundle_name: str):
        }
        config.bundles = current_bundles_config
        config.write()
        _sync_max_instance(os.getcwd(), verbose=False)
        bundles_list()
        _sync_max_instance(directory, verbose=False)
        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))
@@ -705,13 +690,40 @@ def bundles_add(bundle_name: str):
                    }
                    config.bundles = current_bundles_config
                    config.write()
                    _sync_max_instance(os.getcwd(), verbose=False)
                    _sync_max_instance(directory, verbose=False)
                    bundles_list()


@app.command(help=_("Ajoute un fichier ou un dossier XML à l'instance de Max en cours"))
def feed(feed_path: Path):
    check_cwd_is_max()
@app.command(help=_("Supprime un bundle"))
def bundles_remove(
    bundle_name: str,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    config = max_config(directory)
    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))
        raise typer.Exit(code=1)
    for active_bundle_name, active_bundle_url in config.bundles.items():
        if active_bundle_name.strip() != bundle_name.strip():
            keep_bundles[active_bundle_name] = active_bundle_url
    config.bundles = keep_bundles
    config.write()
    bundles_list()


@app.command(help=_("Ajoute un fichier ou un dossier XML"))
def feed(
    feed_path: Path,
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    java_bin_path = ensure_java()
    feed_path = Path(feed_path).resolve()
    if not feed_path.is_file() and not feed_path.is_dir():
@@ -720,7 +732,7 @@ def feed(feed_path: Path):
    process_args = [
        str(java_bin_path),
        "-cp",
        cp_paths(),
        cp_paths(directory),
        "-Xmx2g",
        "org.basex.BaseX",
        "if(not(db:exists('max'))) then db:create('max') else ()",
@@ -731,7 +743,7 @@ def feed(feed_path: Path):
        process_args = [
            str(java_bin_path),
            "-cp",
            cp_paths(),
            cp_paths(directory),
            "-Xmx2g",
            "org.basex.BaseX",
            f"db:put('max', '{feed_path}', '{feed_path.name}')",
@@ -744,7 +756,7 @@ def feed(feed_path: Path):
            process_args = [
                str(java_bin_path),
                "-cp",
                cp_paths(),
                cp_paths(directory),
                "-Xmx2g",
                "org.basex.BaseX",
                f"db:put('max', '{f}', '{str(f)[offset:]}')",
@@ -753,27 +765,61 @@ def feed(feed_path: Path):
        subprocess.run(process_args)


@app.command(help=_("Lance le client de BaseX"))
def basex():
    check_cwd_is_max()
@app.command(
    help=_(
        "Initialisation d'une instance existante de MaX.\n\nTélécharge et installe les dépendances nécessaires ainsi que les bundles configurés."
    )
)
def sync(
    directory: Annotated[
        Optional[Path], typer.Argument(help=_("chemin vers un dossier"))
    ] = os.getcwd(),
):
    ensure_java()
    root_directory, is_max = ensure_available_max_directory(directory)

    if is_max:
        _sync_max_instance(root_directory)
        climax_db.save(MaxInstall(None, str(root_directory.resolve())))
        climax_db.commit()
    else:
        print(
            _(
                "Le dossier ne contient pas une instance de MaX. Utiliser la commande new"
            )
        )
        raise typer.Exit()


@app.command(help=_("Lance le shell de BaseX"))
def basex(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    java_bin_path = ensure_java()
    process_args = [
        str(java_bin_path),
        "-cp",
        cp_paths(),
        cp_paths(directory),
        "-Xmx2g",
        "org.basex.BaseX",
    ]
    subprocess.run(process_args)


@app.command(help=_("Liste les templates du bundle de vocabulaire en cours."))
def templates_list():
    check_cwd_is_max()
    config = max_config()
@app.command(help=_("Liste les templates du bundle de vocabulaire"))
def templates_list(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    config = max_config(directory)
    vocab_bundle = config.vocabulary_bundle
    tpl_dir = Path(
        os.getcwd(),
        directory,
        ".max",
        "basex",
        "webapp",
@@ -792,16 +838,18 @@ def templates_list():


@app.command(
    help=_(
        "Liste les fichiers statiques (js, css, etc) du bundle de vocabulaire en cours."
    )
    help=_("Liste les fichiers statiques (js, css, etc) du bundle de vocabulaire")
)
def static_list():
    check_cwd_is_max()
    config = max_config()
def static_list(
    directory: Annotated[
        str, typer.Option(help=_("Dossier du projet MaX"))
    ] = os.getcwd(),
):
    check_dir_is_max(directory)
    config = max_config(directory)
    vocab_bundle = config.vocabulary_bundle
    tpl_dir = Path(
        os.getcwd(),
        directory,
        ".max",
        "basex",
        "webapp",
@@ -833,6 +881,14 @@ def projects():
    console.print(table)


@app.command(help=_("Efface le cache de climax"))
def cache_clear():
    for p in CACHE_DIR.iterdir():
        if p.is_file():
            p.unlink(missing_ok=True)
    shutil.rmtree(Path(USER_MAX_DIR, "max"), ignore_errors=True)


# @app.command(help=_("Application web (test)"))
# def ui(max_dir: str = None):
#    from .gui import app
+6 −0
Original line number Diff line number Diff line
@@ -10,6 +10,12 @@ logger = logging.getLogger(__name__)

GITLAB_API_READ_TOKEN = "glpat-5BCYCid1WaxxBgax-7VW"

CLI_MAIN_HELP = """Utilitaire en ligne de commande pour la gestion des projets MaX.

La plupart des commandes sont à lancer dans le dossier du projet MaX, à l'exception des
commandes new, projects et cache-clear qui peuvent être lancées aussi en dehors d'un dossier MaX.
"""

WELCOME_PAGE = """
<h1>Bienvenue dans MaX !</h1>
<p>