AUTHOR
FIREWHEEL Team
DONE
DESCRIPTION
Install a new repository of Model Components.
The repository should be an existing directory on the filesystem.
The path may be specified absolute or relative.
If the path does not exist, an error message is printed.

Some Model Components may provide an additional installation details which can be executed to perform other setup steps (e.g. installing an extra python package or downloading an external VM resource).
This takes the form of either a ``INSTALL`` directory with a ``vars.yml`` and a ``tasks.yml`` that are Ansible tasks which can be executed.
Alternatively, it can be a single ``INSTALL`` script that can be can be any executable file type as defined by a `shebang <https://en.wikipedia.org/wiki/Shebang_(Unix)>`_ line.

.. warning::

    The execution of Model Component ``INSTALL`` scripts can be a **DANGEROUS** operation.
    Please ensure that you **fully trust** the repository developer prior to executing these scripts.

.. seealso::

    See :ref:`mc_install` for more information on ``INSTALL`` scripts.

When installing a Model Component, users will have a variety of choices to select:

- ``y`` - yes, install execute this script
- ``n`` - no, do not execute this script
- ``v`` - view, see the script text
- ``vc`` - view color, see the script text with color support, must use a system pager which supports this behavior (e.g. ``PAGER='less -R'``)
- ``q`` - quit, exit immediately


Arguments
+++++++++

.. option:: path

    The path to the repository to install. If not provided, will assume that the user desires to run the installation scripts for all existing repositories (assuming the ``-s`` flag is also used).

.. option::  -s, --script

    Run any Model Component-specific installation scripts. The install script should be in the Model Components top-level directory in a file called ``INSTALL``.

.. option::  -i, --insecure

    Automatically run all Model Component INSTALL scripts. Must be run with the :option:`repository install -s` option to take effect.

.. option:: -h, --help

    Show a help message and exit.


Examples
++++++++

``firewheel repository install <directory>``

``firewheel repository install --script <directory>``

DONE
RUN LocalPython ON control
#!/usr/bin/env python

import time
import argparse
from pathlib import Path

from rich.table import Column
from rich.console import Console
from rich.progress import Progress, TextColumn

from firewheel.control.repository_db import RepositoryDb
from firewheel.control.model_component_install import ModelComponentInstall
from firewheel.control.model_component_iterator import ModelComponentIterator


def install_all_mcs(insecure=False):
    """
    Ask the user to run the install script for all the Model Components
    of all repositories.

    Args:
        insecure (bool): Whether to automatically install all Model Components without asking
            for each one.
    """
    repo_db = RepositoryDb()
    for repo in repo_db.list_repositories():
        run_install_script(repo, insecure)


def install_repo(repo):
    """
    Install the given FIREWHEEL repository.

    Args:
        repo (dict): A dictionary object which can be added as a new FIREWHEEL repository.
    """
    repo_db = RepositoryDb()
    ret = repo_db.add_repository(repo)
    if ret:
        Console().print("[green]Repository successfully installed!")
    else:
        Console().print("[yellow]Duplicate repositories cannot be installed!")


def run_install_script(repo, insecure):
    """
    Ask the user to run the install script for all the Model Components
    of the provided repository.

    Args:
        repo (dict): A dictionary object which can be added as a new FIREWHEEL repository.
        insecure (bool): Whether to automatically install all Model Components without asking
            for each one.

    Returns:
        bool: True if all MCs were installed successfully, False otherwise
    """
    console = Console()
    failed = []
    for mc in ModelComponentIterator(iter([repo])):
        mci = ModelComponentInstall(mc)

        if insecure:
            success = mci.run_install_script(insecure=True)
            if not success:
                failed.append(mc.name)
        else:
            success = mci.run_install_script()
            if not success:
                failed.append(mc.name)

    if failed:
        console.print(f"[red]The following MC's failed to fully install in {repo['path']}: {failed}")
        return False

    console.print(f"[green]All model components for {repo['path']} were installed or skipped!")
    return True


if __name__ == "__main__":
    con = Console()
    parser = argparse.ArgumentParser(
        description="Install a new model component repository!"
    )
    parser.add_argument(
        "--script",
        "-s",
        required=False,
        action="store_true",
        dest="script",
        help=str(
            "Run any Model Component-specific installation scripts. "
            "The install script should be in the Model Components top-level directory in a "
            "file called `INSTALL`."
        ),
        default=False,
    )
    parser.add_argument(
        "--insecure",
        "-i",
        required=False,
        action="store_true",
        help=str(
            "Automatically run all Model Component INSTALL scripts. "
            "Must be run with the '-s/--script' option to take effect."
        ),
        default=False,
    )
    parser.add_argument("path", help="The path to the repository to install.", nargs="?")

    args = parser.parse_args()

    PATH = None
    REPO_ENTRY = None
    if args.path:
        PATH = Path(args.path)
        REPO_ENTRY = {"path": str(PATH.resolve())}
        SUCCESS = True
    else:
        SUCCESS = False

    if args.script:
        con.print(
            "[yellow]Running any Model Component install scripts. This could be "
            "a [red]DANGEROUS[/red] operation!!! "
            "[yellow]Ensure that you completely trust the Model Component "
            "creator before continuing!"
        )
        if args.insecure:
            DESCRIPTION = str(
                "[b red]You used INSECURE mode. This will automatically run "
                "all Model Component install scripts. Pausing for: "
            )
            text_column = TextColumn(  # noqa: FS003
                (
                    "[progress.description]{task.description}"  # noqa: FS003
                    "[cyan]{task.remaining}[/cyan] seconds."  # noqa: FS003
                ),
                table_column=Column(ratio=1),
            )
            progress = Progress(text_column, expand=True)

            with progress as p:
                task = p.add_task(DESCRIPTION, total=10)
                for _ in range(10):
                    p.update(task, advance=1, refresh=True)
                    time.sleep(1)
            con.print("[green]Continuing...")  # noqa: FS003

        if PATH is None:
            install_all_mcs(args.insecure)
            SUCCESS = False
        else:
            SUCCESS = run_install_script(REPO_ENTRY, args.insecure)

    if SUCCESS:
        install_repo(REPO_ENTRY)
DONE
