Ejemplo n.º 1
0
def version():
    """Print the current version of the cli and platform."""
    server_versions = get_server_versions()
    cli_version = get_current_version()
    Printer.print_header("Current cli version: {}.".format(cli_version))
    Printer.print_header("Supported versions:")
    dict_tabulate(server_versions.to_dict())
Ejemplo n.º 2
0
def set(**kwargs):  # pylint:disable=redefined-builtin
    """Set the global config values.

    Example:

    \b
    $ polyaxon config set --host=localhost
    """
    try:
        _config = ClientConfigManager.get_config_or_default()
    except Exception as e:
        handle_cli_error(e, message="Polyaxon load configuration.")
        Printer.print_header(
            "You can reset your config by running: polyaxon config purge"
        )
        sys.exit(1)

    for key, value in kwargs.items():
        if value is not None:
            setattr(_config, key, value)

    ClientConfigManager.set_config(_config)
    Printer.print_success("Config was updated.")
    # Reset cli config
    CliConfigManager.purge()
Ejemplo n.º 3
0
def get_project_details(project):
    if project.description:
        Printer.print_header("Project description:")
        click.echo("{}\n".format(project.description))

    response = dict_to_tabulate(project.to_dict(),
                                humanize_values=True,
                                exclude_attrs=["description"])

    Printer.print_header("Project info:")
    dict_tabulate(response)
Ejemplo n.º 4
0
def get_model_version_details(response):
    metadata = response.metadata
    response = dict_to_tabulate(
        response.to_dict(), humanize_values=True, exclude_attrs=["metadata"]
    )

    Printer.print_header("Model version info:")
    dict_tabulate(response)

    if metadata:
        Printer.print_header("Metadata:")
        click.echo(metadata)
Ejemplo n.º 5
0
def whoami():
    """Show current logged Polyaxon user."""
    try:
        polyaxon_client = PolyaxonClient()
        user = polyaxon_client.users_v1.get_user()
    except (ApiException, HTTPError) as e:
        handle_cli_error(e, message="Could not load user info.")
        sys.exit(1)

    response = dict_to_tabulate(user.to_dict(), exclude_attrs=["role"])

    Printer.print_header("User info:")
    dict_tabulate(response)
Ejemplo n.º 6
0
def ls(owner, limit, offset):
    """List projects.

    Uses /docs/core/cli/#caching
    """
    owner = owner or AuthConfigManager.get_value("username")
    if not owner:
        Printer.print_error(
            "Please login first or provide a valid owner --owner. "
            "`polyaxon login --help`")
        sys.exit(1)

    try:
        polyaxon_client = ProjectClient(owner=owner)
        response = polyaxon_client.list(limit=limit, offset=offset)
    except (ApiException, HTTPError) as e:
        handle_cli_error(e, message="Could not get list of projects.")
        sys.exit(1)

    meta = get_meta_response(response)
    if meta:
        Printer.print_header("Projects for current user")
        Printer.print_header("Navigation:")
        dict_tabulate(meta)
    else:
        Printer.print_header("No projects found for current user")

    objects = list_dicts_to_tabulate(
        [o.to_dict() for o in response.results],
        humanize_values=True,
        exclude_attrs=["uuid", "description"],
    )
    if objects:
        Printer.print_header("Projects:")
        dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 7
0
def get_run_details(run):  # pylint:disable=redefined-outer-name
    if run.description:
        Printer.print_header("Run description:")
        click.echo("{}\n".format(run.description))

    if run.inputs:
        Printer.print_header("Run inputs:")
        dict_tabulate(run.inputs)

    if run.outputs:
        Printer.print_header("Run outputs:")
        dict_tabulate(run.outputs)

    response = Printer.add_status_color(run.to_dict())
    response = dict_to_tabulate(
        response,
        humanize_values=True,
        exclude_attrs=[
            "project",
            "description",
            "readme",
            "content",
            "raw_content",
            "inputs",
            "outputs",
            "is_managed",
        ],
    )

    Printer.print_header("Run info:")
    dict_tabulate(response)
Ejemplo n.º 8
0
def get_component_version_details(response):
    content = response.content
    response = dict_to_tabulate(response.to_dict(),
                                humanize_values=True,
                                exclude_attrs=["content"])

    Printer.print_header("Component info:")
    dict_tabulate(response)

    if content:
        specification = get_specification(data=content)
        get_specification_details(specification)
    else:
        Printer.print_warning(
            "This component version does not have any polyaxonfile content!")
Ejemplo n.º 9
0
def dashboard(ctx, yes, url):
    """Open this operation's dashboard details in browser."""
    owner, project_name = get_project_or_local(ctx.obj.get("project"), is_cli=True)
    dashboard_url = clean_host(settings.CLIENT_CONFIG.host)
    project_url = "{}/{}/{}/".format(dashboard_url, owner, project_name)
    if url:
        Printer.print_header("The dashboard is available at: {}".format(project_url))
        sys.exit(0)
    if not yes:
        click.confirm(
            "Dashboard page will now open in your browser. Continue?",
            abort=True,
            default=True,
        )
    click.launch(project_url)
Ejemplo n.º 10
0
    def _handle_run_statuses():
        if not conditions:
            return
        Printer.print_header("Latest status:")
        latest_status = Printer.add_status_color({"status": status},
                                                 status_key="status")
        click.echo("{}\n".format(latest_status["status"]))

        objects = list_dicts_to_tabulate([
            Printer.add_status_color(o.to_dict(), status_key="type")
            for o in conditions
        ])
        if objects:
            Printer.print_header("Conditions:")
            dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 11
0
def dashboard(ctx, _project, yes, url):
    """Open this operation's dashboard details in browser."""
    owner, project_name = get_project_or_local(
        _project or ctx.obj.get("project"), is_cli=True
    )
    project_url = get_dashboard_url(subpath="{}/{}".format(owner, project_name))
    if url:
        Printer.print_header("The dashboard is available at: {}".format(project_url))
        sys.exit(0)
    if not yes:
        click.confirm(
            "Dashboard page will now open in your browser. Continue?",
            abort=True,
            default=True,
        )
    click.launch(project_url)
Ejemplo n.º 12
0
def whoami():
    """Show current logged Polyaxon Cloud or Polyaxon EE user."""
    try:
        polyaxon_client = PolyaxonClient()
        user = polyaxon_client.users_v1.get_user()
    except ApiException as e:
        if e.status == 403:
            session_expired()
        handle_cli_error(e,
                         message="Could not get the user info.",
                         sys_exit=True)
    except (ApiException, HTTPError) as e:
        handle_cli_error(e, message="Could not load user info.", sys_exit=True)

    response = dict_to_tabulate(user.to_dict(), exclude_attrs=["role"])
    Printer.print_header("User info:")
    dict_tabulate(response)
Ejemplo n.º 13
0
def port_forward(port, namespace, deployment_type, release_name):
    """If you deploy Polyaxon using ClusterIP, you can use this command
    to access the gateway through `localhost:port`.
    """
    from polyaxon.deploy.operators.kubectl import KubectlOperator

    if not port and deployment_type in [
            DeploymentTypes.MICRO_K8S,
            DeploymentTypes.MINIKUBE,
    ]:
        port = 31833
    port = port or 8000
    namespace = namespace or "polyaxon"
    release_name = release_name or "polyaxon"

    kubectl = KubectlOperator()
    args = [
        "port-forward",
        "-n",
        namespace,
        "svc/{}-polyaxon-gateway".format(release_name),
        "{}:80".format(port),
    ]

    try:
        _config = ClientConfigManager.get_config_or_default()
    except Exception as e:
        handle_cli_error(e, message="Polyaxon load configuration.")
        Printer.print_header(
            "You can reset your config by running: polyaxon config purge")
        sys.exit(1)

    _config.host = "http://localhost:{}".format(port)
    ClientConfigManager.set_config(_config)
    CliConfigManager.purge()
    AuthConfigManager.purge()
    UserConfigManager.purge()
    Printer.print_header("Client configuration is updated!")
    Printer.print_success("Polyaxon will be available at: {}".format(
        _config.host))
    stdout = kubectl.execute(args=args,
                             is_json=False,
                             stream=settings.CLIENT_CONFIG.debug)
    click.echo(stdout)
Ejemplo n.º 14
0
def dashboard(model, version, yes, url):
    """Open this operation's dashboard details in browser."""
    owner, model_registry, model_version, is_version = get_info(model, version)
    subpath = ("{}/registry/{}/versions?version={}".format(
        owner, model_registry, model_version) if is_version else
               "{}/registry/{}".format(owner, model_registry))

    registry_url = get_dashboard_url(subpath=subpath)
    if url:
        Printer.print_header(
            "The dashboard is available at: {}".format(registry_url))
        sys.exit(0)
    if not yes:
        click.confirm(
            "Dashboard page will now open in your browser. Continue?",
            abort=True,
            default=True,
        )
    click.launch(registry_url)
Ejemplo n.º 15
0
def dashboard(component, version, yes, url):
    """Open this operation's dashboard details in browser."""
    owner, component_hub, component_version, is_version = get_info(component, version)
    subpath = (
        "{}/hub/{}/versions?version={}".format(owner, component_hub, component_version)
        if is_version
        else "{}/hub/{}".format(owner, component_hub)
    )

    hub_url = get_dashboard_url(subpath=subpath, use_cloud=settings.CLI_CONFIG.is_ce)
    if url:
        Printer.print_header("The dashboard is available at: {}".format(hub_url))
        sys.exit(0)
    if not yes:
        click.confirm(
            "Dashboard page will now open in your browser. Continue?",
            abort=True,
            default=True,
        )
    click.launch(hub_url)
Ejemplo n.º 16
0
 def _sync(run_uuid: str):
     Printer.print_header(f"Syncing offline run {uid} ...")
     client = RunClient(run_uuid=run_uuid, is_offline=True)
     try:
         client.sync_offline_run(
             load_offline_run=True,
             artifacts_path=offline_path_format.format(run_uuid),
             upload_artifacts=not no_artifacts,
             clean=clean,
         )
     except (
             ApiException,
             HTTPError,
             PolyaxonHTTPError,
             PolyaxonShouldExitError,
             PolyaxonClientException,
     ) as e:
         handle_cli_error(
             e, message="Could not sync offline run `{}`.".format(run_uuid))
         sys.exit(1)
Ejemplo n.º 17
0
def get_eager_matrix_operations(
    content: str,
    compiled_operation: V1CompiledOperation,
    is_cli: bool = False,
) -> List[V1Operation]:
    is_supported_in_eager_mode(compiled_operation)

    try:
        import numpy as np
    except ImportError as e:
        if is_cli:
            Printer.print_error(
                "numpy is required for the eager mode, "
                "please run 'pip install polyaxon[numpy]'",
                sys_exit=True,
            )
        raise e

    from polyaxon.polytune.search_managers.grid_search.manager import GridSearchManager
    from polyaxon.polytune.search_managers.mapping.manager import MappingManager
    from polyaxon.polytune.search_managers.random_search.manager import (
        RandomSearchManager, )

    if compiled_operation.has_random_search_matrix:
        suggestions = RandomSearchManager(
            compiled_operation.matrix).get_suggestions()
    elif compiled_operation.has_grid_search_matrix:
        suggestions = GridSearchManager(
            compiled_operation.matrix).get_suggestions()
    elif compiled_operation.has_mapping_matrix:
        suggestions = MappingManager(
            compiled_operation.matrix).get_suggestions()
    else:
        raise PolyaxonSchemaError(
            "Received a bad configuration, eager mode not supported, "
            "I should not be here!")
    if is_cli:
        Printer.print_header("Creating {} operations".format(len(suggestions)))
    return get_ops_from_suggestions(content=content,
                                    compiled_operation=compiled_operation,
                                    suggestions=suggestions)
Ejemplo n.º 18
0
def get_component_details(polyaxonfile, specification):
    if specification.name:
        Printer.print_header("Component description:")
        click.echo("{}\n".format(specification.description))
    if specification.description:
        Printer.print_header("Component description:")
        click.echo("{}\n".format(specification.description))

    if specification.inputs:
        Printer.print_header("Component inputs:")
        objects = list_dicts_to_tabulate([i.to_dict() for i in specification.inputs])
        dict_tabulate(objects, is_list_dict=True)

    if specification.outputs:
        Printer.print_header("Component outputs:")
        objects = list_dicts_to_tabulate([o.to_dict() for o in specification.outputs])
        dict_tabulate(objects, is_list_dict=True)
        dict_tabulate(specification.outputs)

    Printer.print_header("Component:")
    click.echo(polyaxonfile)
Ejemplo n.º 19
0
def ls(owner, query, sort, limit, offset):
    """List projects.

    Uses /docs/core/cli/#caching
    """
    owner = owner or settings.AUTH_CONFIG.username
    if not owner and (not settings.CLI_CONFIG or settings.CLI_CONFIG.is_ce):
        owner = DEFAULT
    if not owner:
        Printer.print_error(
            "Please login first or provide a valid owner --owner. "
            "`polyaxon login --help`")
        sys.exit(1)

    try:
        polyaxon_client = ProjectClient(owner=owner)
        response = polyaxon_client.list(limit=limit,
                                        offset=offset,
                                        query=query,
                                        sort=sort)
    except (ApiException, HTTPError) as e:
        handle_cli_error(e, message="Could not get list of projects.")
        sys.exit(1)

    meta = get_meta_response(response)
    if meta:
        Printer.print_header("Projects for current user")
        Printer.print_header("Navigation:")
        dict_tabulate(meta)
    else:
        Printer.print_header("No projects found for current user")

    objects = list_dicts_to_tabulate(
        [o.to_dict() for o in response.results],
        humanize_values=True,
        exclude_attrs=[
            "uuid",
            "readme",
            "description",
            "is_deleted",
            "owner",
            "user_email",
            "teams",
            "role",
            "settings",
        ],
    )
    if objects:
        Printer.print_header("Projects:")
        dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 20
0
def show():
    """Show the current cli, client, and user configs."""
    _config = ClientConfigManager.get_config_or_default()
    Printer.print_header("Client config:")
    dict_tabulate(_config.to_dict())
    _config = CliConfigManager.get_config_or_default()
    if _config:
        Printer.print_header("CLI config:")
        if _config.current_version:
            click.echo("Version {}".format(_config.current_version))
        else:
            Printer.print_warning("This cli is not configured.")
        if _config.installation:
            config_installation = dict_to_tabulate(
                _config.installation,
                humanize_values=True,
                exclude_attrs=["hmac", "auth", "host"],
            )
            dict_tabulate(config_installation)
        else:
            Printer.print_warning(
                "This cli is not connected to a Polyaxon Host.")
    _config = UserConfigManager.get_config_or_default()
    if _config:
        Printer.print_header("User config:")
        config_user = dict_to_tabulate(
            _config.to_dict(),
            humanize_values=True,
            exclude_attrs=["theme"],
        )
        dict_tabulate(config_user)
Ejemplo n.º 21
0
    def _get_run_statuses():
        try:
            for status, conditions in get_run_statuses(owner, project_name,
                                                       run_uuid, watch):
                if not conditions:
                    continue
                Printer.print_header("Latest status:")
                latest_status = Printer.add_status_color({"status": status},
                                                         status_key="status")
                click.echo("{}\n".format(latest_status["status"]))

                objects = list_dicts_to_tabulate([
                    Printer.add_status_color(o.to_dict(), status_key="type")
                    for o in conditions
                ])
                if objects:
                    Printer.print_header("Conditions:")
                    dict_tabulate(objects, is_list_dict=True)
        except (ApiException, HTTPError, PolyaxonClientException) as e:
            handle_cli_error(
                e, message="Could get status for run `{}`.".format(run_uuid))
            sys.exit(1)
Ejemplo n.º 22
0
def runs(ctx, limit, offset):
    """List bookmarked runs for user.

    Uses [Caching](/references/polyaxon-cli/#caching)

    Examples:

    \b
    ```bash
    $ polyaxon bookmark experiments
    ```

    \b
    ```bash
    $ polyaxon bookmark -u adam experiments
    ```
    """
    user = get_username_or_local(ctx.obj.get("username"))

    try:
        params = get_query_params(limit=limit, offset=offset)
        polyaxon_client = PolyaxonClient()
        response = polyaxon_client.runs_v1.list_bookmarked_runs(user, **params)
    except (ApiException, HTTPError) as e:
        handle_cli_error(
            e,
            message="Could not get bookmarked experiments for user `{}`.".format(user),
        )
        sys.exit(1)

    meta = get_meta_response(response)
    if meta:
        Printer.print_header("Bookmarked experiments for user `{}`.".format(user))
        Printer.print_header("Navigation:")
        dict_tabulate(meta)
    else:
        Printer.print_header(
            "No bookmarked experiments found for user `{}`.".format(user)
        )

    objects = [
        Printer.add_status_color(o.to_light_dict(humanize_values=True))
        for o in response.results
    ]
    objects = list_dicts_to_tabulate(objects)
    if objects:
        Printer.print_header("Experiments:")
        dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 23
0
def code(ctx):
    """Download code for run.

    Uses [Caching](/references/polyaxon-cli/#caching)

    Examples:

    \b
    ```bash
    $ polyaxon runs -uid=8aac02e3a62a4f0aaa257c59da5eab80 code
    ```
    """
    owner, project_name, run_uuid = get_project_run_or_local(
        ctx.obj.get("project"), ctx.obj.get("run_uuid"))
    try:
        polyaxon_client = PolyaxonClient()
        code_ref = polyaxon_client.runs_v1.get_run_code_refs(
            owner, project_name, run_uuid)
        commit = None
        if code_ref:
            commit = code_ref.commit
            Printer.print_header(
                "Run has code ref: `{}`, downloading ...".format(commit))
        else:
            Printer.print_warning(
                "Run has no code ref, downloading latest code...")
        PolyaxonClient().project.download_repo(owner,
                                               project_name,
                                               commit=commit)
    except (ApiException, HTTPError) as e:
        handle_cli_error(
            e,
            message="Could not download outputs for run `{}`.".format(
                run_uuid))
        sys.exit(1)
    Printer.print_success("Files downloaded.")
Ejemplo n.º 24
0
def version(check):
    """Print the current version of the cli and platform."""
    Printer.print_header("Current cli version: {}.".format(pkg.VERSION))
    if check:
        config = set_versions_config()
        Printer.print_header("Platform:")
        dict_tabulate(config.installation)
        Printer.print_header("compatibility versions:")
        dict_tabulate(config.compatibility)
        check_cli_version(config)
Ejemplo n.º 25
0
def ls(owner, query, sort, limit, offset):
    """List projects.

    Uses /docs/core/cli/#caching
    """
    owner = owner or get_local_owner(is_cli=True)
    if not owner:
        Printer.print_error("Please provide a valid owner: --owner/-o.")
        sys.exit(1)

    try:
        polyaxon_client = ProjectClient(owner=owner)
        response = polyaxon_client.list(limit=limit,
                                        offset=offset,
                                        query=query,
                                        sort=sort)
    except (ApiException, HTTPError) as e:
        handle_cli_error(e, message="Could not get list of projects.")
        sys.exit(1)

    meta = get_meta_response(response)
    if meta:
        Printer.print_header("Projects for owner {}".format(owner))
        Printer.print_header("Navigation:")
        dict_tabulate(meta)
    else:
        Printer.print_header("No projects found for owner {}".format(owner))

    objects = list_dicts_to_tabulate(
        [o.to_dict() for o in response.results],
        humanize_values=True,
        exclude_attrs=[
            "uuid",
            "readme",
            "description",
            "owner",
            "user_email",
            "role",
            "settings",
        ],
    )
    if objects:
        Printer.print_header("Projects:")
        dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 26
0
def service(ctx, yes, external, url):
    """Open the operation service in browser.

    N.B. The operation must have a run kind service, otherwise it will raise an error.

    You can open the service embedded in Polyaxon UI or using the real service URL,
    please use the `--external` flag.
    """
    owner, project_name, run_uuid = get_project_run_or_local(
        ctx.obj.get("project"),
        ctx.obj.get("run_uuid"),
        is_cli=True,
    )
    client = RunClient(owner=owner, project=project_name, run_uuid=run_uuid)
    client.refresh_data()
    if client.run_data.kind != V1RunKind.SERVICE:
        Printer.print_warning("Command expected a operations of "
                              "kind `service` received kind: {}!".format(
                                  client.run_data.kind))
        sys.exit(1)
    dashboard_url = settings.CLIENT_CONFIG.host

    Printer.print_header("Waiting for running condition ...")
    client.wait_for_condition(statuses=[V1Statuses.RUNNING], print_status=True)

    client.refresh_data()
    namespace = client.run_data.settings.namespace

    run_url = "{}/{}/{}/runs/{}/service".format(dashboard_url, owner,
                                                project_name, run_uuid)
    service_endpoint = SERVICES_V1
    if client.run_data.meta_info.get("rewrite_path", False):
        service_endpoint = REWRITE_SERVICES_V1
    external_run_url = "{}/{}/{}/{}/{}/runs/{}/".format(
        dashboard_url, service_endpoint, namespace, owner, project_name,
        run_uuid)
    if url:
        Printer.print_header(
            "The service will be available at: {}".format(run_url))
        Printer.print_header(
            "You can also view it in an external link at: {}".format(
                external_run_url))
        sys.exit(0)
    if not yes:
        click.confirm(
            "Dashboard page will now open in your browser. Continue?",
            abort=True,
            default=True,
        )
    if external:
        click.launch(external_run_url)
        sys.exit(0)
    click.launch(run_url)
Ejemplo n.º 27
0
    def list_versions():
        component_info = "<owner: {}> <component: {}>".format(
            owner, component_hub)
        try:
            polyaxon_client = get_current_or_public_client()
            params = get_query_params(limit=limit,
                                      offset=offset,
                                      query=query,
                                      sort=sort)
            response = polyaxon_client.component_hub_v1.list_component_versions(
                owner, component_hub, **params)
        except (ApiException, HTTPError) as e:
            message = "Could not get list of component version."
            handle_cli_error(e, message=message)
            sys.exit(1)

        meta = get_meta_response(response)
        if meta:
            Printer.print_header("Versions for {}".format(component_info))
            Printer.print_header("Navigation:")
            dict_tabulate(meta)
        else:
            Printer.print_header(
                "No version found for {}".format(component_info))

        objects = list_dicts_to_tabulate(
            [o.to_dict() for o in response.results],
            humanize_values=True,
            exclude_attrs=[
                "uuid",
                "readme",
                "description",
                "owner",
                "owner",
                "role",
                "settings",
                "content",
                "live_state",
            ],
        )
        if objects:
            Printer.print_header("Component versions:")
            dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 28
0
def get_specification_details(specification):
    if specification.inputs:
        Printer.print_header("Component inputs:")
        objects = list_dicts_to_tabulate([i.to_dict() for i in specification.inputs])
        dict_tabulate(objects, is_list_dict=True)

    if specification.outputs:
        Printer.print_header("Component outputs:")
        objects = list_dicts_to_tabulate([o.to_dict() for o in specification.outputs])
        dict_tabulate(objects, is_list_dict=True)

    Printer.print_header("Content:")
    click.echo(specification.to_dict())
Ejemplo n.º 29
0
    def list_models():
        try:
            polyaxon_client = PolyaxonClient()
            params = get_query_params(limit=limit,
                                      offset=offset,
                                      query=query,
                                      sort=sort)
            response = polyaxon_client.model_registry_v1.list_model_registries(
                owner, **params)
        except (ApiException, HTTPError) as e:
            message = "Could not get list of models."
            handle_cli_error(e, message=message)
            sys.exit(1)

        meta = get_meta_response(response)
        if meta:
            Printer.print_header("Models for owner {}".format(owner))
            Printer.print_header("Navigation:")
            dict_tabulate(meta)
        else:
            Printer.print_header(
                "No model registry found for owner {}".format(owner))

        objects = list_dicts_to_tabulate(
            [o.to_dict() for o in response.results],
            humanize_values=True,
            exclude_attrs=[
                "uuid",
                "readme",
                "description",
                "owner",
                "role",
                "settings",
                "live_state",
            ],
        )
        if objects:
            Printer.print_header("Models:")
            dict_tabulate(objects, is_list_dict=True)
Ejemplo n.º 30
0
def version(check):
    """Print the current version of the cli and platform."""
    Printer.print_header("Current cli version: {}.".format(pkg.VERSION))
    if check:
        config = set_versions_config()
        Printer.print_header("Platform:")
        config_installation = dict_to_tabulate(
            config.installation,
            humanize_values=True,
            exclude_attrs=["hmac", "auth", "host"],
        )
        dict_tabulate(config_installation)
        Printer.print_header("Compatibility versions:")
        dict_tabulate(config.compatibility)
        check_cli_version(config)