thestage 0.5.471__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thestage/.env +5 -5
- thestage/__init__.py +3 -4
- thestage/__main__.py +9 -9
- thestage/cli_command.py +56 -56
- thestage/cli_command_helper.py +51 -51
- thestage/color_scheme/color_scheme.py +7 -7
- thestage/config/__init__.py +18 -18
- thestage/config/config_storage.py +5 -5
- thestage/config/env_base.py +7 -7
- thestage/controllers/__init__.py +0 -0
- thestage/controllers/base_controller.py +67 -67
- thestage/controllers/config_controller.py +137 -137
- thestage/controllers/container_controller.py +389 -389
- thestage/controllers/instance_controller.py +183 -183
- thestage/controllers/project_controller.py +810 -783
- thestage/controllers/utils_controller.py +32 -32
- thestage/debug_main.dist.py +28 -28
- thestage/entities/__init__.py +0 -0
- thestage/entities/container.py +17 -17
- thestage/entities/enums/__init__.py +0 -0
- thestage/entities/enums/order_direction_type.py +6 -6
- thestage/entities/enums/shell_type.py +7 -7
- thestage/entities/enums/tail_output_type.py +6 -6
- thestage/entities/enums/yes_no_response.py +7 -7
- thestage/entities/file_item.py +27 -27
- thestage/entities/project_inference_simulator.py +18 -18
- thestage/entities/project_inference_simulator_model.py +16 -16
- thestage/entities/project_task.py +19 -19
- thestage/entities/rented_instance.py +19 -19
- thestage/entities/self_hosted_instance.py +18 -18
- thestage/exceptions/__init__.py +0 -0
- thestage/exceptions/auth_exception.py +6 -6
- thestage/exceptions/base_exception.py +13 -13
- thestage/exceptions/business_logic_exception.py +6 -6
- thestage/exceptions/config_exception.py +6 -6
- thestage/exceptions/file_system_exception.py +6 -6
- thestage/exceptions/git_access_exception.py +17 -17
- thestage/exceptions/remote_server_exception.py +24 -24
- thestage/git/ProgressPrinter.py +22 -22
- thestage/helpers/__init__.py +0 -0
- thestage/helpers/error_handler.py +115 -115
- thestage/helpers/exception_hook.py +14 -14
- thestage/helpers/logger/__init__.py +0 -0
- thestage/helpers/logger/app_logger.py +50 -50
- thestage/helpers/ssh_util.py +38 -38
- thestage/i18n/en_GB/messages.po +947 -947
- thestage/i18n/translation.py +9 -9
- thestage/main.py +36 -36
- thestage/services/.env +6 -6
- thestage/services/__init__.py +0 -0
- thestage/services/abstract_mapper.py +9 -9
- thestage/services/abstract_service.py +87 -87
- thestage/services/app_config_service.py +52 -52
- thestage/services/clients/__init__.py +0 -0
- thestage/services/clients/git/__init__.py +0 -0
- thestage/services/clients/git/git_client.py +436 -331
- thestage/services/clients/thestage_api/__init__.py +0 -0
- thestage/services/clients/thestage_api/api_client.py +718 -720
- thestage/services/clients/thestage_api/core/api_client_core.py +108 -108
- thestage/services/clients/thestage_api/core/http_client_exception.py +12 -12
- thestage/services/clients/thestage_api/dtos/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/base_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +19 -19
- thestage/services/clients/thestage_api/dtos/container_param_request.py +11 -11
- thestage/services/clients/thestage_api/dtos/container_response.py +67 -67
- thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +10 -10
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/docker_container_mapping.py +10 -10
- thestage/services/clients/thestage_api/dtos/entity_filter_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/enums/__init__.py +0 -0
- thestage/services/clients/thestage_api/dtos/enums/container_pending_action.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/container_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/cpu_type.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/currency_type.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/disk_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/drive_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/gpu_name.py +8 -8
- thestage/services/clients/thestage_api/dtos/enums/inference_model_status.py +9 -9
- thestage/services/clients/thestage_api/dtos/enums/inference_simulator_status.py +15 -15
- thestage/services/clients/thestage_api/dtos/enums/instance_rented_status.py +17 -17
- thestage/services/clients/thestage_api/dtos/enums/instance_type.py +7 -7
- thestage/services/clients/thestage_api/dtos/enums/location_region.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/power_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/provider_name.py +11 -11
- thestage/services/clients/thestage_api/dtos/enums/selfhosted_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/enums/task_execution_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/enums/task_status.py +12 -12
- thestage/services/clients/thestage_api/dtos/frontend_status.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_request.py +14 -14
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +12 -12
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_response.py +13 -13
- thestage/services/clients/thestage_api/dtos/inference_simulator_model_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/inference_simulator_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/installed_service.py +17 -17
- thestage/services/clients/thestage_api/dtos/instance_detected_gpus.py +20 -20
- thestage/services/clients/thestage_api/dtos/instance_rented_response.py +71 -71
- thestage/services/clients/thestage_api/dtos/logging_controller/docker_container_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +13 -13
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/logging_controller/task_log_stream_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +21 -21
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_response.py +14 -14
- thestage/services/clients/thestage_api/dtos/paginated_entity_list.py +11 -11
- thestage/services/clients/thestage_api/dtos/pagination_data.py +10 -10
- thestage/services/clients/thestage_api/dtos/price_definition.py +14 -14
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_response.py +6 -6
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +15 -15
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +13 -14
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_response.py +10 -10
- thestage/services/clients/thestage_api/dtos/project_response.py +32 -32
- thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +56 -56
- thestage/services/clients/thestage_api/dtos/sftp_path_helper.py +13 -13
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +8 -8
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_response.py +11 -11
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_request.py +7 -7
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +10 -10
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/task_controller/task_status_localized_map_response.py +9 -9
- thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +12 -12
- thestage/services/clients/thestage_api/dtos/user_controller/user_profile.py +12 -12
- thestage/services/clients/thestage_api/dtos/validate_token_response.py +11 -11
- thestage/services/config_provider/__init__.py +0 -0
- thestage/services/config_provider/config_provider.py +237 -237
- thestage/services/connect/connect_service.py +196 -196
- thestage/services/connect/dto/remote_server_config.py +9 -9
- thestage/services/container/__init__.py +0 -0
- thestage/services/container/container_service.py +374 -374
- thestage/services/container/mapper/__init__.py +0 -0
- thestage/services/container/mapper/container_mapper.py +30 -30
- thestage/services/core_files/config_entity.py +26 -26
- thestage/services/filesystem_service.py +133 -133
- thestage/services/instance/__init__.py +0 -0
- thestage/services/instance/instance_service.py +303 -303
- thestage/services/instance/mapper/__init__.py +0 -0
- thestage/services/instance/mapper/instance_mapper.py +24 -24
- thestage/services/instance/mapper/selfhosted_mapper.py +33 -33
- thestage/services/logging/byte_print_style.py +5 -5
- thestage/services/logging/dto/log_message.py +15 -15
- thestage/services/logging/dto/log_type.py +6 -6
- thestage/services/logging/exception/log_polling_exception.py +6 -6
- thestage/services/logging/logging_constants.py +3 -3
- thestage/services/logging/logging_service.py +367 -367
- thestage/services/project/__init__.py +0 -0
- thestage/services/project/dto/inference_simulator_dto.py +22 -22
- thestage/services/project/dto/inference_simulator_model_dto.py +20 -20
- thestage/services/project/dto/project_config.py +14 -14
- thestage/services/project/mapper/__init__.py +0 -0
- thestage/services/project/mapper/project_inference_simulator_mapper.py +21 -21
- thestage/services/project/mapper/project_inference_simulator_model_mapper.py +21 -21
- thestage/services/project/mapper/project_task_mapper.py +22 -22
- thestage/services/project/project_service.py +1253 -1241
- thestage/services/remote_server_service.py +609 -609
- thestage/services/service_factory.py +97 -97
- thestage/services/task/dto/task_dto.py +40 -40
- thestage/services/validation_service.py +61 -61
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/LICENSE.txt +12 -12
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/METADATA +1 -1
- thestage-0.6.2.dist-info/RECORD +176 -0
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/WHEEL +1 -1
- thestage/debug_tests.py +0 -12
- thestage/services/clients/.DS_Store +0 -0
- thestage-0.5.471.dist-info/RECORD +0 -178
- {thestage-0.5.471.dist-info → thestage-0.6.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,1241 +1,1253 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import time
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Optional, List
|
|
6
|
-
|
|
7
|
-
import json
|
|
8
|
-
|
|
9
|
-
import boto3
|
|
10
|
-
import click
|
|
11
|
-
import typer
|
|
12
|
-
from git import Commit
|
|
13
|
-
from tabulate import tabulate
|
|
14
|
-
|
|
15
|
-
from thestage.entities.project_inference_simulator import ProjectInferenceSimulatorEntity
|
|
16
|
-
from thestage.entities.project_inference_simulator_model import ProjectInferenceSimulatorModelEntity
|
|
17
|
-
from thestage.entities.project_task import ProjectTaskEntity
|
|
18
|
-
from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
|
|
19
|
-
from thestage.services.clients.thestage_api.dtos.enums.inference_model_status import InferenceModelStatus
|
|
20
|
-
from thestage.services.clients.thestage_api.dtos.enums.inference_simulator_status import InferenceSimulatorStatus
|
|
21
|
-
from thestage.color_scheme.color_scheme import ColorScheme
|
|
22
|
-
from thestage.entities.enums.yes_no_response import YesOrNoResponse
|
|
23
|
-
from thestage.exceptions.git_access_exception import GitAccessException
|
|
24
|
-
from thestage.i18n.translation import __
|
|
25
|
-
from thestage.services.clients.git.git_client import GitLocalClient
|
|
26
|
-
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
27
|
-
from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
|
|
28
|
-
from thestage.services.clients.thestage_api.dtos.inference_controller.deploy_inference_model_to_instance_response import \
|
|
29
|
-
DeployInferenceModelToInstanceResponse
|
|
30
|
-
from thestage.services.clients.thestage_api.dtos.inference_controller.deploy_inference_model_to_sagemaker_response import \
|
|
31
|
-
DeployInferenceModelToSagemakerResponse
|
|
32
|
-
from thestage.services.clients.thestage_api.dtos.inference_controller.get_inference_simulator_response import \
|
|
33
|
-
GetInferenceSimulatorResponse
|
|
34
|
-
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
35
|
-
from thestage.services.clients.thestage_api.dtos.project_controller.project_push_inference_simulator_model_response import \
|
|
36
|
-
ProjectPushInferenceSimulatorModelResponse
|
|
37
|
-
from thestage.services.clients.thestage_api.dtos.project_controller.project_run_task_response import \
|
|
38
|
-
ProjectRunTaskResponse
|
|
39
|
-
from thestage.services.clients.thestage_api.dtos.project_controller.project_start_inference_simulator_response import \
|
|
40
|
-
ProjectStartInferenceSimulatorResponse
|
|
41
|
-
from thestage.services.clients.thestage_api.dtos.project_response import ProjectDto
|
|
42
|
-
from thestage.services.clients.thestage_api.dtos.task_controller.task_view_response import TaskViewResponse
|
|
43
|
-
from thestage.services.filesystem_service import FileSystemService
|
|
44
|
-
from thestage.services.project.dto.inference_simulator_dto import InferenceSimulatorDto
|
|
45
|
-
from thestage.services.project.dto.inference_simulator_model_dto import InferenceSimulatorModelDto
|
|
46
|
-
from thestage.services.project.mapper.project_inference_simulator_mapper import ProjectInferenceSimulatorMapper
|
|
47
|
-
from thestage.services.project.mapper.project_inference_simulator_model_mapper import \
|
|
48
|
-
ProjectInferenceSimulatorModelMapper
|
|
49
|
-
from thestage.services.task.dto.task_dto import TaskDto
|
|
50
|
-
from thestage.services.project.dto.project_config import ProjectConfig
|
|
51
|
-
from thestage.services.project.mapper.project_task_mapper import ProjectTaskMapper
|
|
52
|
-
from thestage.services.remote_server_service import RemoteServerService
|
|
53
|
-
from thestage.services.abstract_service import AbstractService
|
|
54
|
-
from thestage.helpers.error_handler import error_handler
|
|
55
|
-
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
56
|
-
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
57
|
-
from rich import print
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class ProjectService(AbstractService):
|
|
61
|
-
__thestage_api_client: TheStageApiClient = None
|
|
62
|
-
__config_provider: ConfigProvider = None
|
|
63
|
-
|
|
64
|
-
def __init__(
|
|
65
|
-
self,
|
|
66
|
-
thestage_api_client: TheStageApiClient,
|
|
67
|
-
config_provider: ConfigProvider,
|
|
68
|
-
remote_server_service: RemoteServerService,
|
|
69
|
-
file_system_service: FileSystemService,
|
|
70
|
-
git_local_client: GitLocalClient,
|
|
71
|
-
):
|
|
72
|
-
self.__thestage_api_client = thestage_api_client
|
|
73
|
-
self.__remote_server_service = remote_server_service
|
|
74
|
-
self.__file_system_service = file_system_service
|
|
75
|
-
self.__git_local_client = git_local_client
|
|
76
|
-
self.__project_task_mapper = ProjectTaskMapper()
|
|
77
|
-
self.__config_provider = config_provider
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@error_handler()
|
|
81
|
-
def init_project(
|
|
82
|
-
self,
|
|
83
|
-
project_slug: str,
|
|
84
|
-
):
|
|
85
|
-
config = self.__config_provider.get_config()
|
|
86
|
-
project: Optional[ProjectDto] = self.__thestage_api_client.get_project_by_slug(
|
|
87
|
-
slug=project_slug,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if not project:
|
|
91
|
-
typer.echo('Project not found')
|
|
92
|
-
raise typer.Exit(1)
|
|
93
|
-
|
|
94
|
-
is_git_folder = self.__git_local_client.is_present_local_git(
|
|
95
|
-
path=config.runtime.working_directory,
|
|
96
|
-
)
|
|
97
|
-
if is_git_folder:
|
|
98
|
-
has_remote = self.__git_local_client.has_remote(
|
|
99
|
-
path=config.runtime.working_directory,
|
|
100
|
-
)
|
|
101
|
-
if has_remote:
|
|
102
|
-
typer.echo(__('You have local repo with remote, we can not work with this'))
|
|
103
|
-
raise typer.Exit(1)
|
|
104
|
-
|
|
105
|
-
if not project.git_repository_url:
|
|
106
|
-
typer.echo(__('Sketch dont have git repository url'))
|
|
107
|
-
raise typer.Exit(1)
|
|
108
|
-
|
|
109
|
-
if project.last_commit_hash or project.last_commit_description:
|
|
110
|
-
continue_with_non_empty_repo: YesOrNoResponse = typer.prompt(
|
|
111
|
-
text=__('Remote repository is probably not empty: latest commit is "{commit_description}" (sha: {commit_hash})\nDo you wish to continue?').format(commit_description=project.last_commit_description, commit_hash=project.last_commit_hash),
|
|
112
|
-
show_choices=True,
|
|
113
|
-
default=YesOrNoResponse.YES.value,
|
|
114
|
-
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
115
|
-
show_default=True,
|
|
116
|
-
)
|
|
117
|
-
if continue_with_non_empty_repo == YesOrNoResponse.NO:
|
|
118
|
-
typer.echo(__('Project init aborted'))
|
|
119
|
-
raise typer.Exit(0)
|
|
120
|
-
|
|
121
|
-
deploy_ssh_key = self.__thestage_api_client.get_project_deploy_ssh_key(
|
|
122
|
-
slug=project.slug,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
deploy_key_path = self.__config_provider.save_project_deploy_ssh_key(
|
|
126
|
-
deploy_ssh_key=deploy_ssh_key,
|
|
127
|
-
project_slug=project.slug,
|
|
128
|
-
project_id=project.id,
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
if is_git_folder:
|
|
132
|
-
has_changes = self.__git_local_client.has_changes_with_untracked(
|
|
133
|
-
path=config.runtime.working_directory,
|
|
134
|
-
)
|
|
135
|
-
if has_changes:
|
|
136
|
-
typer.echo(__('You local repo has changes and not empty, please create empty folder'))
|
|
137
|
-
raise typer.Exit(1)
|
|
138
|
-
else:
|
|
139
|
-
repo = self.__git_local_client.init_repository(
|
|
140
|
-
path=config.runtime.working_directory,
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
is_remote_added = self.__git_local_client.add_remote_to_repo(
|
|
144
|
-
path=config.runtime.working_directory,
|
|
145
|
-
remote_url=project.git_repository_url,
|
|
146
|
-
remote_name=project.git_repository_name,
|
|
147
|
-
)
|
|
148
|
-
if not is_remote_added:
|
|
149
|
-
typer.echo(__('We can not add remote, something wrong'))
|
|
150
|
-
raise typer.Exit(2)
|
|
151
|
-
|
|
152
|
-
self.__git_local_client.git_fetch(path=config.runtime.working_directory, deploy_key_path=deploy_key_path)
|
|
153
|
-
|
|
154
|
-
self.__git_local_client.init_gitignore(path=config.runtime.working_directory)
|
|
155
|
-
|
|
156
|
-
self.__git_local_client.git_add_all(repo_path=config.runtime.working_directory)
|
|
157
|
-
|
|
158
|
-
project_config = ProjectConfig()
|
|
159
|
-
project_config.id = project.id
|
|
160
|
-
project_config.slug = project.slug
|
|
161
|
-
project_config.git_repository_url = project.git_repository_url
|
|
162
|
-
project_config.deploy_key_path = str(deploy_key_path)
|
|
163
|
-
self.__config_provider.save_project_config(project_config=project_config)
|
|
164
|
-
|
|
165
|
-
typer.echo(__("Project successfully initialized at %path%", {"path": config.runtime.working_directory}))
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@error_handler()
|
|
169
|
-
def clone_project(
|
|
170
|
-
self,
|
|
171
|
-
project_slug: str,
|
|
172
|
-
):
|
|
173
|
-
config = self.__config_provider.get_config()
|
|
174
|
-
project: Optional[ProjectDto] = self.__thestage_api_client.get_project_by_slug(
|
|
175
|
-
slug=project_slug,
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
if not project:
|
|
179
|
-
typer.echo('Project not found')
|
|
180
|
-
raise typer.Exit(1)
|
|
181
|
-
|
|
182
|
-
if not self.__file_system_service.is_folder_empty(folder=config.runtime.working_directory, auto_create=True):
|
|
183
|
-
typer.echo(__("Cannot clone: the folder is not empty"))
|
|
184
|
-
raise typer.Exit(1)
|
|
185
|
-
|
|
186
|
-
is_git_folder = self.__git_local_client.is_present_local_git(
|
|
187
|
-
path=config.runtime.working_directory,
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
if is_git_folder:
|
|
191
|
-
typer.echo(__('You have local repo, we can not work with this'))
|
|
192
|
-
raise typer.Exit(1)
|
|
193
|
-
|
|
194
|
-
if not project.git_repository_url:
|
|
195
|
-
typer.echo(__("Unexpected Project error, missing Repository"))
|
|
196
|
-
raise typer.Exit(1)
|
|
197
|
-
|
|
198
|
-
deploy_ssh_key = self.__thestage_api_client.get_project_deploy_ssh_key(slug=project.slug)
|
|
199
|
-
deploy_key_path = self.__config_provider.save_project_deploy_ssh_key(deploy_ssh_key=deploy_ssh_key, project_slug=project.slug, project_id=project.id)
|
|
200
|
-
|
|
201
|
-
try:
|
|
202
|
-
self.__git_local_client.clone(
|
|
203
|
-
url=project.git_repository_url,
|
|
204
|
-
path=config.runtime.working_directory,
|
|
205
|
-
deploy_key_path=deploy_key_path
|
|
206
|
-
)
|
|
207
|
-
self.__git_local_client.init_gitignore(path=config.runtime.working_directory)
|
|
208
|
-
except GitAccessException as ex:
|
|
209
|
-
typer.echo(ex.get_message())
|
|
210
|
-
typer.echo(ex.get_dop_message())
|
|
211
|
-
typer.echo(__(
|
|
212
|
-
"Please check you mail or open this repo url %git_url% and 'Accept invitation'",
|
|
213
|
-
{
|
|
214
|
-
'git_url': ex.get_url()
|
|
215
|
-
}
|
|
216
|
-
))
|
|
217
|
-
raise typer.Exit(1)
|
|
218
|
-
|
|
219
|
-
project_config = ProjectConfig()
|
|
220
|
-
project_config.id = project.id
|
|
221
|
-
project_config.slug = project.slug
|
|
222
|
-
project_config.git_repository_url = project.git_repository_url
|
|
223
|
-
project_config.deploy_key_path = str(deploy_key_path)
|
|
224
|
-
self.__config_provider.save_project_config(project_config=project_config)
|
|
225
|
-
typer.echo(__("Project successfully cloned to %path%", {"path": config.runtime.working_directory}))
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
@error_handler()
|
|
229
|
-
def project_run_task(
|
|
230
|
-
self,
|
|
231
|
-
run_command: str,
|
|
232
|
-
task_title: Optional[str] = None,
|
|
233
|
-
commit_hash: Optional[str] = None,
|
|
234
|
-
docker_container_slug: Optional[str] = None,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
default
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
typer.echo(
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
typer.echo(__("
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
project_slug
|
|
654
|
-
|
|
655
|
-
row
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
data
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
project_slug
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
typer.
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
typer.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
)
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
if self.__git_local_client.
|
|
802
|
-
self.__git_local_client.
|
|
803
|
-
path=config.runtime.working_directory,
|
|
804
|
-
|
|
805
|
-
)
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
typer.echo("
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
project_config
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
project_config
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
if
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
)
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
raise typer.Exit(1)
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
typer.
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
raise typer.Exit(1)
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
"
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
)
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
typer.echo(
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
typer.echo(
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
self.
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, List
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
import boto3
|
|
10
|
+
import click
|
|
11
|
+
import typer
|
|
12
|
+
from git import Commit
|
|
13
|
+
from tabulate import tabulate
|
|
14
|
+
|
|
15
|
+
from thestage.entities.project_inference_simulator import ProjectInferenceSimulatorEntity
|
|
16
|
+
from thestage.entities.project_inference_simulator_model import ProjectInferenceSimulatorModelEntity
|
|
17
|
+
from thestage.entities.project_task import ProjectTaskEntity
|
|
18
|
+
from thestage.services.clients.thestage_api.core.http_client_exception import HttpClientException
|
|
19
|
+
from thestage.services.clients.thestage_api.dtos.enums.inference_model_status import InferenceModelStatus
|
|
20
|
+
from thestage.services.clients.thestage_api.dtos.enums.inference_simulator_status import InferenceSimulatorStatus
|
|
21
|
+
from thestage.color_scheme.color_scheme import ColorScheme
|
|
22
|
+
from thestage.entities.enums.yes_no_response import YesOrNoResponse
|
|
23
|
+
from thestage.exceptions.git_access_exception import GitAccessException
|
|
24
|
+
from thestage.i18n.translation import __
|
|
25
|
+
from thestage.services.clients.git.git_client import GitLocalClient
|
|
26
|
+
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
27
|
+
from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
|
|
28
|
+
from thestage.services.clients.thestage_api.dtos.inference_controller.deploy_inference_model_to_instance_response import \
|
|
29
|
+
DeployInferenceModelToInstanceResponse
|
|
30
|
+
from thestage.services.clients.thestage_api.dtos.inference_controller.deploy_inference_model_to_sagemaker_response import \
|
|
31
|
+
DeployInferenceModelToSagemakerResponse
|
|
32
|
+
from thestage.services.clients.thestage_api.dtos.inference_controller.get_inference_simulator_response import \
|
|
33
|
+
GetInferenceSimulatorResponse
|
|
34
|
+
from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
|
|
35
|
+
from thestage.services.clients.thestage_api.dtos.project_controller.project_push_inference_simulator_model_response import \
|
|
36
|
+
ProjectPushInferenceSimulatorModelResponse
|
|
37
|
+
from thestage.services.clients.thestage_api.dtos.project_controller.project_run_task_response import \
|
|
38
|
+
ProjectRunTaskResponse
|
|
39
|
+
from thestage.services.clients.thestage_api.dtos.project_controller.project_start_inference_simulator_response import \
|
|
40
|
+
ProjectStartInferenceSimulatorResponse
|
|
41
|
+
from thestage.services.clients.thestage_api.dtos.project_response import ProjectDto
|
|
42
|
+
from thestage.services.clients.thestage_api.dtos.task_controller.task_view_response import TaskViewResponse
|
|
43
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
44
|
+
from thestage.services.project.dto.inference_simulator_dto import InferenceSimulatorDto
|
|
45
|
+
from thestage.services.project.dto.inference_simulator_model_dto import InferenceSimulatorModelDto
|
|
46
|
+
from thestage.services.project.mapper.project_inference_simulator_mapper import ProjectInferenceSimulatorMapper
|
|
47
|
+
from thestage.services.project.mapper.project_inference_simulator_model_mapper import \
|
|
48
|
+
ProjectInferenceSimulatorModelMapper
|
|
49
|
+
from thestage.services.task.dto.task_dto import TaskDto
|
|
50
|
+
from thestage.services.project.dto.project_config import ProjectConfig
|
|
51
|
+
from thestage.services.project.mapper.project_task_mapper import ProjectTaskMapper
|
|
52
|
+
from thestage.services.remote_server_service import RemoteServerService
|
|
53
|
+
from thestage.services.abstract_service import AbstractService
|
|
54
|
+
from thestage.helpers.error_handler import error_handler
|
|
55
|
+
from thestage.services.clients.thestage_api.api_client import TheStageApiClient
|
|
56
|
+
from thestage.services.config_provider.config_provider import ConfigProvider
|
|
57
|
+
from rich import print
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ProjectService(AbstractService):
|
|
61
|
+
__thestage_api_client: TheStageApiClient = None
|
|
62
|
+
__config_provider: ConfigProvider = None
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
thestage_api_client: TheStageApiClient,
|
|
67
|
+
config_provider: ConfigProvider,
|
|
68
|
+
remote_server_service: RemoteServerService,
|
|
69
|
+
file_system_service: FileSystemService,
|
|
70
|
+
git_local_client: GitLocalClient,
|
|
71
|
+
):
|
|
72
|
+
self.__thestage_api_client = thestage_api_client
|
|
73
|
+
self.__remote_server_service = remote_server_service
|
|
74
|
+
self.__file_system_service = file_system_service
|
|
75
|
+
self.__git_local_client = git_local_client
|
|
76
|
+
self.__project_task_mapper = ProjectTaskMapper()
|
|
77
|
+
self.__config_provider = config_provider
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@error_handler()
|
|
81
|
+
def init_project(
|
|
82
|
+
self,
|
|
83
|
+
project_slug: str,
|
|
84
|
+
):
|
|
85
|
+
config = self.__config_provider.get_config()
|
|
86
|
+
project: Optional[ProjectDto] = self.__thestage_api_client.get_project_by_slug(
|
|
87
|
+
slug=project_slug,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if not project:
|
|
91
|
+
typer.echo('Project not found')
|
|
92
|
+
raise typer.Exit(1)
|
|
93
|
+
|
|
94
|
+
is_git_folder = self.__git_local_client.is_present_local_git(
|
|
95
|
+
path=config.runtime.working_directory,
|
|
96
|
+
)
|
|
97
|
+
if is_git_folder:
|
|
98
|
+
has_remote = self.__git_local_client.has_remote(
|
|
99
|
+
path=config.runtime.working_directory,
|
|
100
|
+
)
|
|
101
|
+
if has_remote:
|
|
102
|
+
typer.echo(__('You have local repo with remote, we can not work with this'))
|
|
103
|
+
raise typer.Exit(1)
|
|
104
|
+
|
|
105
|
+
if not project.git_repository_url:
|
|
106
|
+
typer.echo(__('Sketch dont have git repository url'))
|
|
107
|
+
raise typer.Exit(1)
|
|
108
|
+
|
|
109
|
+
if project.last_commit_hash or project.last_commit_description:
|
|
110
|
+
continue_with_non_empty_repo: YesOrNoResponse = typer.prompt(
|
|
111
|
+
text=__('Remote repository is probably not empty: latest commit is "{commit_description}" (sha: {commit_hash})\nDo you wish to continue?').format(commit_description=project.last_commit_description, commit_hash=project.last_commit_hash),
|
|
112
|
+
show_choices=True,
|
|
113
|
+
default=YesOrNoResponse.YES.value,
|
|
114
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
115
|
+
show_default=True,
|
|
116
|
+
)
|
|
117
|
+
if continue_with_non_empty_repo == YesOrNoResponse.NO:
|
|
118
|
+
typer.echo(__('Project init aborted'))
|
|
119
|
+
raise typer.Exit(0)
|
|
120
|
+
|
|
121
|
+
deploy_ssh_key = self.__thestage_api_client.get_project_deploy_ssh_key(
|
|
122
|
+
slug=project.slug,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
deploy_key_path = self.__config_provider.save_project_deploy_ssh_key(
|
|
126
|
+
deploy_ssh_key=deploy_ssh_key,
|
|
127
|
+
project_slug=project.slug,
|
|
128
|
+
project_id=project.id,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if is_git_folder:
|
|
132
|
+
has_changes = self.__git_local_client.has_changes_with_untracked(
|
|
133
|
+
path=config.runtime.working_directory,
|
|
134
|
+
)
|
|
135
|
+
if has_changes:
|
|
136
|
+
typer.echo(__('You local repo has changes and not empty, please create empty folder'))
|
|
137
|
+
raise typer.Exit(1)
|
|
138
|
+
else:
|
|
139
|
+
repo = self.__git_local_client.init_repository(
|
|
140
|
+
path=config.runtime.working_directory,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
is_remote_added = self.__git_local_client.add_remote_to_repo(
|
|
144
|
+
path=config.runtime.working_directory,
|
|
145
|
+
remote_url=project.git_repository_url,
|
|
146
|
+
remote_name=project.git_repository_name,
|
|
147
|
+
)
|
|
148
|
+
if not is_remote_added:
|
|
149
|
+
typer.echo(__('We can not add remote, something wrong'))
|
|
150
|
+
raise typer.Exit(2)
|
|
151
|
+
|
|
152
|
+
self.__git_local_client.git_fetch(path=config.runtime.working_directory, deploy_key_path=deploy_key_path)
|
|
153
|
+
|
|
154
|
+
self.__git_local_client.init_gitignore(path=config.runtime.working_directory)
|
|
155
|
+
|
|
156
|
+
self.__git_local_client.git_add_all(repo_path=config.runtime.working_directory)
|
|
157
|
+
|
|
158
|
+
project_config = ProjectConfig()
|
|
159
|
+
project_config.id = project.id
|
|
160
|
+
project_config.slug = project.slug
|
|
161
|
+
project_config.git_repository_url = project.git_repository_url
|
|
162
|
+
project_config.deploy_key_path = str(deploy_key_path)
|
|
163
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
164
|
+
|
|
165
|
+
typer.echo(__("Project successfully initialized at %path%", {"path": config.runtime.working_directory}))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@error_handler()
|
|
169
|
+
def clone_project(
|
|
170
|
+
self,
|
|
171
|
+
project_slug: str,
|
|
172
|
+
):
|
|
173
|
+
config = self.__config_provider.get_config()
|
|
174
|
+
project: Optional[ProjectDto] = self.__thestage_api_client.get_project_by_slug(
|
|
175
|
+
slug=project_slug,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if not project:
|
|
179
|
+
typer.echo('Project not found')
|
|
180
|
+
raise typer.Exit(1)
|
|
181
|
+
|
|
182
|
+
if not self.__file_system_service.is_folder_empty(folder=config.runtime.working_directory, auto_create=True):
|
|
183
|
+
typer.echo(__("Cannot clone: the folder is not empty"))
|
|
184
|
+
raise typer.Exit(1)
|
|
185
|
+
|
|
186
|
+
is_git_folder = self.__git_local_client.is_present_local_git(
|
|
187
|
+
path=config.runtime.working_directory,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if is_git_folder:
|
|
191
|
+
typer.echo(__('You have local repo, we can not work with this'))
|
|
192
|
+
raise typer.Exit(1)
|
|
193
|
+
|
|
194
|
+
if not project.git_repository_url:
|
|
195
|
+
typer.echo(__("Unexpected Project error, missing Repository"))
|
|
196
|
+
raise typer.Exit(1)
|
|
197
|
+
|
|
198
|
+
deploy_ssh_key = self.__thestage_api_client.get_project_deploy_ssh_key(slug=project.slug)
|
|
199
|
+
deploy_key_path = self.__config_provider.save_project_deploy_ssh_key(deploy_ssh_key=deploy_ssh_key, project_slug=project.slug, project_id=project.id)
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
self.__git_local_client.clone(
|
|
203
|
+
url=project.git_repository_url,
|
|
204
|
+
path=config.runtime.working_directory,
|
|
205
|
+
deploy_key_path=deploy_key_path
|
|
206
|
+
)
|
|
207
|
+
self.__git_local_client.init_gitignore(path=config.runtime.working_directory)
|
|
208
|
+
except GitAccessException as ex:
|
|
209
|
+
typer.echo(ex.get_message())
|
|
210
|
+
typer.echo(ex.get_dop_message())
|
|
211
|
+
typer.echo(__(
|
|
212
|
+
"Please check you mail or open this repo url %git_url% and 'Accept invitation'",
|
|
213
|
+
{
|
|
214
|
+
'git_url': ex.get_url()
|
|
215
|
+
}
|
|
216
|
+
))
|
|
217
|
+
raise typer.Exit(1)
|
|
218
|
+
|
|
219
|
+
project_config = ProjectConfig()
|
|
220
|
+
project_config.id = project.id
|
|
221
|
+
project_config.slug = project.slug
|
|
222
|
+
project_config.git_repository_url = project.git_repository_url
|
|
223
|
+
project_config.deploy_key_path = str(deploy_key_path)
|
|
224
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
225
|
+
typer.echo(__("Project successfully cloned to %path%", {"path": config.runtime.working_directory}))
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@error_handler()
|
|
229
|
+
def project_run_task(
|
|
230
|
+
self,
|
|
231
|
+
run_command: str,
|
|
232
|
+
task_title: Optional[str] = None,
|
|
233
|
+
commit_hash: Optional[str] = None,
|
|
234
|
+
docker_container_slug: Optional[str] = None,
|
|
235
|
+
files_to_add: Optional[str] = None,
|
|
236
|
+
is_skip_auto_commit: Optional[bool] = False,
|
|
237
|
+
) -> Optional[TaskDto]:
|
|
238
|
+
config = self.__config_provider.get_config()
|
|
239
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
240
|
+
if not project_config:
|
|
241
|
+
typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.", {"path": config.runtime.working_directory}))
|
|
242
|
+
raise typer.Exit(1)
|
|
243
|
+
|
|
244
|
+
if not docker_container_slug and not project_config.default_container_uid:
|
|
245
|
+
typer.echo(__('Docker container unique ID is required'))
|
|
246
|
+
raise typer.Exit(1)
|
|
247
|
+
|
|
248
|
+
container_slug_for_task = docker_container_slug if docker_container_slug else project_config.default_container_uid
|
|
249
|
+
|
|
250
|
+
if not docker_container_slug:
|
|
251
|
+
typer.echo(f"Using default docker container for this project: '{container_slug_for_task}'")
|
|
252
|
+
|
|
253
|
+
container: DockerContainerDto = self.__thestage_api_client.get_container(
|
|
254
|
+
container_slug=container_slug_for_task,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if container is None:
|
|
258
|
+
typer.echo(f"Could not find container '{container_slug_for_task}'")
|
|
259
|
+
if project_config.default_container_uid == container_slug_for_task:
|
|
260
|
+
project_config.default_container_uid = None
|
|
261
|
+
project_config.prompt_for_default_container = True
|
|
262
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
263
|
+
typer.echo(f"Default container settings were reset")
|
|
264
|
+
raise typer.Exit(1)
|
|
265
|
+
|
|
266
|
+
if container.project_id != project_config.id:
|
|
267
|
+
typer.echo(f"Provided container '{docker_container_slug}' is not related to project '{project_config.slug}'")
|
|
268
|
+
raise typer.Exit(1)
|
|
269
|
+
|
|
270
|
+
if (project_config.prompt_for_default_container is None or project_config.prompt_for_default_container) and docker_container_slug and (project_config.default_container_uid != docker_container_slug):
|
|
271
|
+
set_default_container_slug: str = typer.prompt(
|
|
272
|
+
text=f"Would you like to set '{docker_container_slug}' as a default container for this project installation?",
|
|
273
|
+
show_choices=True,
|
|
274
|
+
default=YesOrNoResponse.YES.value,
|
|
275
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
276
|
+
show_default=True,
|
|
277
|
+
)
|
|
278
|
+
project_config.prompt_for_default_container = False
|
|
279
|
+
if set_default_container_slug == YesOrNoResponse.YES.value:
|
|
280
|
+
project_config.default_container_uid = docker_container_slug
|
|
281
|
+
|
|
282
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
283
|
+
|
|
284
|
+
has_wrong_args = files_to_add and commit_hash or is_skip_auto_commit and commit_hash or files_to_add and is_skip_auto_commit
|
|
285
|
+
|
|
286
|
+
if has_wrong_args:
|
|
287
|
+
warning_msg = f"[{ColorScheme.WARNING.value}][WARNING] You can provide only one of the following arguments: --commit-hash, --files-add, --skip-autocommit[{ColorScheme.WARNING.value}]"
|
|
288
|
+
print(warning_msg)
|
|
289
|
+
raise typer.Exit(1)
|
|
290
|
+
|
|
291
|
+
if not is_skip_auto_commit and not commit_hash:
|
|
292
|
+
is_git_folder = self.__git_local_client.is_present_local_git(path=config.runtime.working_directory)
|
|
293
|
+
if not is_git_folder:
|
|
294
|
+
typer.echo("Error: working directory does not contain git repository")
|
|
295
|
+
raise typer.Exit(1)
|
|
296
|
+
|
|
297
|
+
is_commit_allowed: bool = True
|
|
298
|
+
has_changes = self.__git_local_client.has_changes_with_untracked(
|
|
299
|
+
path=config.runtime.working_directory,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if self.__git_local_client.is_head_detached(path=config.runtime.working_directory):
|
|
303
|
+
is_commit_allowed = False
|
|
304
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]HEAD is detached[{ColorScheme.GIT_HEADLESS.value}]")
|
|
305
|
+
|
|
306
|
+
is_headless_commits_present = self.__git_local_client.is_head_committed_in_headless_state(path=config.runtime.working_directory)
|
|
307
|
+
if is_headless_commits_present:
|
|
308
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]Current commit was made in detached head state. Cannot use it to run the task. Consider using 'project checkout' command to return to a valid reference.[{ColorScheme.GIT_HEADLESS.value}]")
|
|
309
|
+
raise typer.Exit(1)
|
|
310
|
+
|
|
311
|
+
if has_changes:
|
|
312
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]Local changes detected in detached head state. They will not impact the task execution.[{ColorScheme.GIT_HEADLESS.value}]")
|
|
313
|
+
response: YesOrNoResponse = typer.prompt(
|
|
314
|
+
text=__('Continue?'),
|
|
315
|
+
show_choices=True,
|
|
316
|
+
default=YesOrNoResponse.YES.value,
|
|
317
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
318
|
+
show_default=True,
|
|
319
|
+
)
|
|
320
|
+
if response == YesOrNoResponse.NO:
|
|
321
|
+
raise typer.Exit(0)
|
|
322
|
+
|
|
323
|
+
if is_commit_allowed:
|
|
324
|
+
if not self.__git_local_client.add_files_with_size_limit_or_warn(config.runtime.working_directory, files_to_add):
|
|
325
|
+
warning_msg = f"[{ColorScheme.WARNING.value}][WARNING] Task was not started [{ColorScheme.WARNING.value}]"
|
|
326
|
+
print(warning_msg)
|
|
327
|
+
raise typer.Exit(1)
|
|
328
|
+
|
|
329
|
+
diff_stat = self.__git_local_client.git_diff_stat(repo_path=config.runtime.working_directory)
|
|
330
|
+
|
|
331
|
+
if has_changes and diff_stat:
|
|
332
|
+
branch_name = self.__git_local_client.get_active_branch_name(config.runtime.working_directory)
|
|
333
|
+
|
|
334
|
+
typer.echo(__('Active branch [%branch_name%] has uncommitted changes: %diff_stat_bottomline%', {
|
|
335
|
+
'diff_stat_bottomline': diff_stat,
|
|
336
|
+
'branch_name': branch_name,
|
|
337
|
+
}))
|
|
338
|
+
|
|
339
|
+
response: str = typer.prompt(
|
|
340
|
+
text=__('Commit changes?'),
|
|
341
|
+
show_choices=True,
|
|
342
|
+
default=YesOrNoResponse.YES.value,
|
|
343
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
344
|
+
show_default=True,
|
|
345
|
+
)
|
|
346
|
+
if response == YesOrNoResponse.NO.value:
|
|
347
|
+
typer.echo("Task cannot use uncommitted changes - aborting")
|
|
348
|
+
raise typer.Exit(0)
|
|
349
|
+
|
|
350
|
+
commit_name = typer.prompt(
|
|
351
|
+
text=__('Please provide commit message'),
|
|
352
|
+
show_choices=False,
|
|
353
|
+
type=str,
|
|
354
|
+
show_default=False,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if commit_name:
|
|
358
|
+
commit_result = self.__git_local_client.commit_local_changes(
|
|
359
|
+
path=config.runtime.working_directory,
|
|
360
|
+
name=commit_name
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
if commit_result:
|
|
364
|
+
# in docs not Commit object, on real - str
|
|
365
|
+
if isinstance(commit_result, str):
|
|
366
|
+
typer.echo(commit_result)
|
|
367
|
+
else:
|
|
368
|
+
typer.echo(__('Cannot commit with empty commit message'))
|
|
369
|
+
raise typer.Exit(0)
|
|
370
|
+
else:
|
|
371
|
+
pass
|
|
372
|
+
# possible to push new empty branch - only that there's a wrong place to do so
|
|
373
|
+
|
|
374
|
+
self.__git_local_client.push_changes(
|
|
375
|
+
path=config.runtime.working_directory,
|
|
376
|
+
deploy_key_path=project_config.deploy_key_path
|
|
377
|
+
)
|
|
378
|
+
typer.echo(__("Pushed changes to remote repository"))
|
|
379
|
+
|
|
380
|
+
if not commit_hash:
|
|
381
|
+
commit = self.__git_local_client.get_current_commit(path=config.runtime.working_directory)
|
|
382
|
+
if not commit or not isinstance(commit, Commit):
|
|
383
|
+
print('[red]Error: No current commit found in the local repository[/red]')
|
|
384
|
+
raise typer.Exit(0)
|
|
385
|
+
commit_hash = commit.hexsha
|
|
386
|
+
else:
|
|
387
|
+
commit = self.__git_local_client.get_commit_by_hash(path=config.runtime.working_directory, commit_hash=commit_hash)
|
|
388
|
+
if not commit or not isinstance(commit, Commit):
|
|
389
|
+
print(f'[red]Error: commit \'{commit_hash}\' was not found in the local repository[/red]')
|
|
390
|
+
raise typer.Exit(0)
|
|
391
|
+
|
|
392
|
+
if not task_title:
|
|
393
|
+
task_title = commit.message.strip() if commit.message else f'Task_{commit_hash}'
|
|
394
|
+
if not commit.message:
|
|
395
|
+
typer.echo(f'Commit message is empty. Task title is set to "{task_title}"')
|
|
396
|
+
|
|
397
|
+
run_task_response: ProjectRunTaskResponse = self.__thestage_api_client.execute_project_task(
|
|
398
|
+
project_slug=project_config.slug,
|
|
399
|
+
docker_container_slug=container_slug_for_task,
|
|
400
|
+
run_command=run_command,
|
|
401
|
+
commit_hash=commit_hash,
|
|
402
|
+
task_title=task_title,
|
|
403
|
+
)
|
|
404
|
+
if run_task_response:
|
|
405
|
+
if run_task_response.message:
|
|
406
|
+
print(f"[{ColorScheme.WARNING.value}]{run_task_response.message}[{ColorScheme.WARNING.value}]")
|
|
407
|
+
if run_task_response.is_success and run_task_response.task:
|
|
408
|
+
typer.echo(f"Task '{run_task_response.task.title}' has been scheduled successfully. Task ID: {run_task_response.task.id}")
|
|
409
|
+
return run_task_response.task
|
|
410
|
+
else:
|
|
411
|
+
typer.echo(f'The task failed with an error: {run_task_response.message}')
|
|
412
|
+
raise typer.Exit(1)
|
|
413
|
+
else:
|
|
414
|
+
typer.echo("The task failed with an error")
|
|
415
|
+
raise typer.Exit(1)
|
|
416
|
+
|
|
417
|
+
@error_handler()
|
|
418
|
+
def cancel_task(self, task_id: int):
|
|
419
|
+
cancel_result = self.__thestage_api_client.cancel_task(
|
|
420
|
+
task_id=task_id,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
if cancel_result.is_success:
|
|
424
|
+
typer.echo(f'Task {task_id} has been canceled')
|
|
425
|
+
else:
|
|
426
|
+
typer.echo(f'Task {task_id} could not be canceled: {cancel_result.message}')
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@error_handler()
|
|
430
|
+
def project_run_inference_simulator(
|
|
431
|
+
self,
|
|
432
|
+
commit_hash: Optional[str] = None,
|
|
433
|
+
rented_instance_unique_id: Optional[str] = None,
|
|
434
|
+
self_hosted_instance_unique_id: Optional[str] = None,
|
|
435
|
+
inference_dir: Optional[str] = None,
|
|
436
|
+
is_skip_installation: Optional[bool] = False,
|
|
437
|
+
files_to_add: Optional[str] = None,
|
|
438
|
+
is_skip_auto_commit: Optional[bool] = False,
|
|
439
|
+
) -> Optional[InferenceSimulatorDto]:
|
|
440
|
+
config = self.__config_provider.get_config()
|
|
441
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
442
|
+
if not project_config:
|
|
443
|
+
typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
|
|
444
|
+
{"path": config.runtime.working_directory}))
|
|
445
|
+
raise typer.Exit(1)
|
|
446
|
+
|
|
447
|
+
if rented_instance_unique_id and self_hosted_instance_unique_id:
|
|
448
|
+
typer.echo(__("Error: Cannot provide both rented and self-hosted instance unique IDs."))
|
|
449
|
+
raise typer.Exit(1)
|
|
450
|
+
|
|
451
|
+
if not rented_instance_unique_id and not self_hosted_instance_unique_id:
|
|
452
|
+
typer.echo(__("Error: Either a rented instance ID or a self-hosted instance unique ID must be provided."))
|
|
453
|
+
raise typer.Exit(1)
|
|
454
|
+
|
|
455
|
+
has_wrong_args = files_to_add and commit_hash or is_skip_auto_commit and commit_hash or files_to_add and is_skip_auto_commit
|
|
456
|
+
|
|
457
|
+
if has_wrong_args:
|
|
458
|
+
warning_msg = f"[{ColorScheme.WARNING.value}][WARNING] You can provide only one of the following arguments: --commit-hash, --files-add, --skip-autocommit[{ColorScheme.WARNING.value}]"
|
|
459
|
+
print(warning_msg)
|
|
460
|
+
raise typer.Exit(1)
|
|
461
|
+
|
|
462
|
+
if not is_skip_auto_commit and not commit_hash:
|
|
463
|
+
is_git_folder = self.__git_local_client.is_present_local_git(path=config.runtime.working_directory)
|
|
464
|
+
if not is_git_folder:
|
|
465
|
+
typer.echo("Error: Working directory does not contain git repository.")
|
|
466
|
+
raise typer.Exit(1)
|
|
467
|
+
|
|
468
|
+
is_commit_allowed: bool = True
|
|
469
|
+
has_changes = self.__git_local_client.has_changes_with_untracked(
|
|
470
|
+
path=config.runtime.working_directory,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if self.__git_local_client.is_head_detached(path=config.runtime.working_directory):
|
|
474
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]HEAD is detached[{ColorScheme.GIT_HEADLESS.value}]")
|
|
475
|
+
|
|
476
|
+
is_headless_commits_present = self.__git_local_client.is_head_committed_in_headless_state(
|
|
477
|
+
path=config.runtime.working_directory)
|
|
478
|
+
if is_headless_commits_present:
|
|
479
|
+
print(
|
|
480
|
+
f"[{ColorScheme.GIT_HEADLESS.value}]Current commit was made in detached head state. Cannot use it to start the inference simulator. Consider using 'project checkout' command to return to a valid reference.[{ColorScheme.GIT_HEADLESS.value}]")
|
|
481
|
+
raise typer.Exit(1)
|
|
482
|
+
|
|
483
|
+
if has_changes:
|
|
484
|
+
print(
|
|
485
|
+
f"[{ColorScheme.GIT_HEADLESS.value}]Local changes detected in detached head state. They will not impact the inference simulator.[{ColorScheme.GIT_HEADLESS.value}]")
|
|
486
|
+
is_commit_allowed = False
|
|
487
|
+
response: YesOrNoResponse = typer.prompt(
|
|
488
|
+
text=__('Continue?'),
|
|
489
|
+
show_choices=True,
|
|
490
|
+
default=YesOrNoResponse.YES.value,
|
|
491
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
492
|
+
show_default=True,
|
|
493
|
+
)
|
|
494
|
+
if response == YesOrNoResponse.NO:
|
|
495
|
+
raise typer.Exit(0)
|
|
496
|
+
|
|
497
|
+
if is_commit_allowed:
|
|
498
|
+
if not self.__git_local_client.add_files_with_size_limit_or_warn(config.runtime.working_directory, files_to_add):
|
|
499
|
+
warning_msg = f"[{ColorScheme.WARNING.value}][WARNING] Inference simulator was not started [{ColorScheme.WARNING.value}]"
|
|
500
|
+
print(warning_msg)
|
|
501
|
+
raise typer.Exit(1)
|
|
502
|
+
|
|
503
|
+
diff_stat = self.__git_local_client.git_diff_stat(repo_path=config.runtime.working_directory)
|
|
504
|
+
|
|
505
|
+
if has_changes and diff_stat:
|
|
506
|
+
branch_name = self.__git_local_client.get_active_branch_name(config.runtime.working_directory)
|
|
507
|
+
typer.echo(__('Active branch [%branch_name%] has uncommitted changes: %diff_stat_bottomline%', {
|
|
508
|
+
'diff_stat_bottomline': diff_stat,
|
|
509
|
+
'branch_name': branch_name,
|
|
510
|
+
}))
|
|
511
|
+
|
|
512
|
+
response: str = typer.prompt(
|
|
513
|
+
text=__('Commit changes?'),
|
|
514
|
+
show_choices=True,
|
|
515
|
+
default=YesOrNoResponse.YES.value,
|
|
516
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
517
|
+
show_default=True,
|
|
518
|
+
)
|
|
519
|
+
if response == YesOrNoResponse.NO.value:
|
|
520
|
+
typer.echo("inference simulator cannot use uncommitted changes - aborting")
|
|
521
|
+
raise typer.Exit(0)
|
|
522
|
+
|
|
523
|
+
commit_name = typer.prompt(
|
|
524
|
+
text=__('Please provide commit message'),
|
|
525
|
+
show_choices=False,
|
|
526
|
+
type=str,
|
|
527
|
+
show_default=False,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
if commit_name:
|
|
531
|
+
commit_result = self.__git_local_client.commit_local_changes(
|
|
532
|
+
path=config.runtime.working_directory,
|
|
533
|
+
name=commit_name
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
if commit_result:
|
|
537
|
+
# in docs not Commit object, on real - str
|
|
538
|
+
if isinstance(commit_result, str):
|
|
539
|
+
typer.echo(commit_result)
|
|
540
|
+
|
|
541
|
+
self.__git_local_client.push_changes(
|
|
542
|
+
path=config.runtime.working_directory,
|
|
543
|
+
deploy_key_path=project_config.deploy_key_path
|
|
544
|
+
)
|
|
545
|
+
typer.echo(__("Pushed changes to remote repository"))
|
|
546
|
+
else:
|
|
547
|
+
typer.echo(__('Cannot commit with empty commit name, your code will run without last changes.'))
|
|
548
|
+
else:
|
|
549
|
+
pass
|
|
550
|
+
# possible to push new empty branch - only that there's a wrong place to do so
|
|
551
|
+
|
|
552
|
+
if not commit_hash:
|
|
553
|
+
commit = self.__git_local_client.get_current_commit(path=config.runtime.working_directory)
|
|
554
|
+
if commit and isinstance(commit, Commit):
|
|
555
|
+
commit_hash = commit.hexsha
|
|
556
|
+
|
|
557
|
+
start_inference_simulator_response: ProjectStartInferenceSimulatorResponse = self.__thestage_api_client.start_project_inference_simulator(
|
|
558
|
+
project_slug=project_config.slug,
|
|
559
|
+
commit_hash=commit_hash,
|
|
560
|
+
rented_instance_unique_id=rented_instance_unique_id,
|
|
561
|
+
self_hosted_instance_unique_id=self_hosted_instance_unique_id,
|
|
562
|
+
inference_dir=inference_dir,
|
|
563
|
+
is_skip_installation=is_skip_installation,
|
|
564
|
+
)
|
|
565
|
+
if start_inference_simulator_response:
|
|
566
|
+
if start_inference_simulator_response.message:
|
|
567
|
+
typer.echo(start_inference_simulator_response.message)
|
|
568
|
+
if start_inference_simulator_response.is_success and start_inference_simulator_response.inferenceSimulator:
|
|
569
|
+
typer.echo("Inference simulator has been scheduled to run successfully.")
|
|
570
|
+
return start_inference_simulator_response.inferenceSimulator
|
|
571
|
+
else:
|
|
572
|
+
typer.echo(__(
|
|
573
|
+
'Inference simulator failed to run with an error: %server_massage%',
|
|
574
|
+
{'server_massage': start_inference_simulator_response.message or ""}
|
|
575
|
+
))
|
|
576
|
+
raise typer.Exit(1)
|
|
577
|
+
else:
|
|
578
|
+
typer.echo(__("Inference simulator failed to run with an error"))
|
|
579
|
+
raise typer.Exit(1)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
@error_handler()
|
|
583
|
+
def project_push_inference_simulator(
|
|
584
|
+
self,
|
|
585
|
+
slug: Optional[str] = None,
|
|
586
|
+
):
|
|
587
|
+
|
|
588
|
+
push_inference_simulator_model_response: ProjectPushInferenceSimulatorModelResponse = self.__thestage_api_client.push_project_inference_simulator_model(
|
|
589
|
+
slug=slug,
|
|
590
|
+
)
|
|
591
|
+
if push_inference_simulator_model_response:
|
|
592
|
+
if push_inference_simulator_model_response.message:
|
|
593
|
+
typer.echo(push_inference_simulator_model_response.message)
|
|
594
|
+
if push_inference_simulator_model_response.is_success:
|
|
595
|
+
typer.echo("Inference simulator has been successfully scheduled to be pushed to S3 and ECR.")
|
|
596
|
+
else:
|
|
597
|
+
typer.echo(__(
|
|
598
|
+
'Failed to push inference simulator with an error: %server_massage%',
|
|
599
|
+
{'server_massage': push_inference_simulator_model_response.message or ""}
|
|
600
|
+
))
|
|
601
|
+
raise typer.Exit(1)
|
|
602
|
+
else:
|
|
603
|
+
typer.echo(__("Failed to push inference simulator with an error"))
|
|
604
|
+
raise typer.Exit(1)
|
|
605
|
+
|
|
606
|
+
@error_handler()
|
|
607
|
+
def project_get_and_save_inference_simulator_metadata(
|
|
608
|
+
self,
|
|
609
|
+
slug: Optional[str] = None,
|
|
610
|
+
file_path: Optional[str] = None,
|
|
611
|
+
):
|
|
612
|
+
get_inference_metadata_response: GetInferenceSimulatorResponse = self.__thestage_api_client.get_inference_simulator(
|
|
613
|
+
slug=slug,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
metadata = get_inference_metadata_response.inferenceSimulator.qlip_serve_metadata
|
|
617
|
+
|
|
618
|
+
if metadata:
|
|
619
|
+
typer.echo("qlip_serve_metadata:")
|
|
620
|
+
typer.echo(json.dumps(json.loads(metadata), indent=4))
|
|
621
|
+
|
|
622
|
+
if not file_path:
|
|
623
|
+
file_path = Path(os.getcwd()) / "metadata.json"
|
|
624
|
+
typer.echo(__("No file path provided. Saving metadata to %file_path%", {"file_path": str(file_path)}))
|
|
625
|
+
|
|
626
|
+
try:
|
|
627
|
+
parsed_metadata = json.loads(metadata)
|
|
628
|
+
|
|
629
|
+
output_file = Path(file_path)
|
|
630
|
+
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
631
|
+
with output_file.open("w", encoding="utf-8") as file:
|
|
632
|
+
json.dump(parsed_metadata, file, indent=4)
|
|
633
|
+
typer.echo(__("Metadata successfully saved to %file_path%", {"file_path": str(file_path)}))
|
|
634
|
+
except Exception as e:
|
|
635
|
+
typer.echo(__("Failed to save metadata to %file_path%. Error: %error%",
|
|
636
|
+
{"file_path": file_path, "error": str(e)}))
|
|
637
|
+
raise typer.Exit(1)
|
|
638
|
+
else:
|
|
639
|
+
typer.echo(__("No qlip_serve_metadata found"))
|
|
640
|
+
raise typer.Exit(1)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
@error_handler()
|
|
644
|
+
def get_project_inference_simulator_list(
|
|
645
|
+
self,
|
|
646
|
+
project_slug: str,
|
|
647
|
+
statuses: List[str],
|
|
648
|
+
row: int = 5,
|
|
649
|
+
page: int = 1,
|
|
650
|
+
) -> PaginatedEntityList[InferenceSimulatorDto]:
|
|
651
|
+
data: Optional[PaginatedEntityList[InferenceSimulatorDto]] = self.__thestage_api_client.get_inference_simulator_list_for_project(
|
|
652
|
+
statuses=statuses,
|
|
653
|
+
project_slug=project_slug,
|
|
654
|
+
page=page,
|
|
655
|
+
limit=row,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
return data
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@error_handler()
|
|
662
|
+
def get_project_inference_simulator_model_list(
|
|
663
|
+
self,
|
|
664
|
+
project_slug: str,
|
|
665
|
+
statuses: List[str],
|
|
666
|
+
row: int = 5,
|
|
667
|
+
page: int = 1,
|
|
668
|
+
) -> PaginatedEntityList[InferenceSimulatorModelDto]:
|
|
669
|
+
data: Optional[
|
|
670
|
+
PaginatedEntityList[InferenceSimulatorModelDto]] = self.__thestage_api_client.get_inference_simulator_model_list_for_project(
|
|
671
|
+
statuses=statuses,
|
|
672
|
+
project_slug=project_slug,
|
|
673
|
+
page=page,
|
|
674
|
+
limit=row,
|
|
675
|
+
)
|
|
676
|
+
|
|
677
|
+
return data
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
@error_handler()
|
|
681
|
+
def get_project_task_list(
|
|
682
|
+
self,
|
|
683
|
+
project_slug: str,
|
|
684
|
+
row: int = 5,
|
|
685
|
+
page: int = 1,
|
|
686
|
+
) -> PaginatedEntityList[TaskDto]:
|
|
687
|
+
data: Optional[PaginatedEntityList[TaskDto]] = self.__thestage_api_client.get_task_list_for_project(
|
|
688
|
+
project_slug=project_slug,
|
|
689
|
+
page=page,
|
|
690
|
+
limit=row,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
return data
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
@error_handler()
|
|
697
|
+
def checkout_project(
|
|
698
|
+
self,
|
|
699
|
+
task_id: Optional[int],
|
|
700
|
+
branch_name: Optional[str],
|
|
701
|
+
):
|
|
702
|
+
config = self.__config_provider.get_config()
|
|
703
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
704
|
+
if not project_config:
|
|
705
|
+
typer.echo(__("This command is only allowed from within an initialized project directory"))
|
|
706
|
+
raise typer.Exit(1)
|
|
707
|
+
|
|
708
|
+
target_commit_hash: Optional[str] = None
|
|
709
|
+
if task_id:
|
|
710
|
+
task_view_response: Optional[TaskViewResponse] = None
|
|
711
|
+
try:
|
|
712
|
+
task_view_response = self.__thestage_api_client.get_task(task_id=task_id,)
|
|
713
|
+
except HttpClientException as e:
|
|
714
|
+
if e.get_status_code() == 400:
|
|
715
|
+
typer.echo(f"Task {task_id} was not found")
|
|
716
|
+
# overriding arguments here
|
|
717
|
+
branch_name = str(task_id)
|
|
718
|
+
task_id = None
|
|
719
|
+
|
|
720
|
+
if task_view_response and task_view_response.task:
|
|
721
|
+
target_commit_hash = task_view_response.task.commit_hash
|
|
722
|
+
if not target_commit_hash:
|
|
723
|
+
typer.echo(f"Provided task ({task_id}) has no commit hash") # possible legacy problems
|
|
724
|
+
raise typer.Exit(1)
|
|
725
|
+
|
|
726
|
+
is_commit_allowed: bool = True
|
|
727
|
+
|
|
728
|
+
if self.__git_local_client.is_head_detached(path=config.runtime.working_directory):
|
|
729
|
+
is_commit_allowed = False
|
|
730
|
+
if self.__git_local_client.is_head_committed_in_headless_state(path=config.runtime.working_directory):
|
|
731
|
+
commit_message = self.__git_local_client.get_current_commit(path=config.runtime.working_directory).message
|
|
732
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]Your current commit '{commit_message.strip()}' was likely created in detached head state. Checking out will discard all changes.[/{ColorScheme.GIT_HEADLESS.value}]")
|
|
733
|
+
response: YesOrNoResponse = typer.prompt(
|
|
734
|
+
text=__('Continue?'),
|
|
735
|
+
show_choices=True,
|
|
736
|
+
default=YesOrNoResponse.YES.value,
|
|
737
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
738
|
+
show_default=True,
|
|
739
|
+
)
|
|
740
|
+
if response == YesOrNoResponse.NO:
|
|
741
|
+
raise typer.Exit(0)
|
|
742
|
+
else:
|
|
743
|
+
if self.__git_local_client.get_active_branch_name(path=config.runtime.working_directory) == branch_name:
|
|
744
|
+
typer.echo(f"You are already at branch '{branch_name}'")
|
|
745
|
+
raise typer.Exit(0)
|
|
746
|
+
|
|
747
|
+
if is_commit_allowed:
|
|
748
|
+
self.__git_local_client.git_add_all(repo_path=config.runtime.working_directory)
|
|
749
|
+
|
|
750
|
+
has_changes = self.__git_local_client.has_changes_with_untracked(
|
|
751
|
+
path=config.runtime.working_directory,
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
if has_changes:
|
|
755
|
+
active_branch_name = self.__git_local_client.get_active_branch_name(config.runtime.working_directory)
|
|
756
|
+
diff_stat = self.__git_local_client.git_diff_stat(repo_path=config.runtime.working_directory)
|
|
757
|
+
typer.echo(__('Active branch [%branch_name%] has uncommitted changes: %diff_stat_bottomline%', {
|
|
758
|
+
'diff_stat_bottomline': diff_stat,
|
|
759
|
+
'branch_name': active_branch_name,
|
|
760
|
+
}))
|
|
761
|
+
|
|
762
|
+
response: str = typer.prompt(
|
|
763
|
+
text=__('Commit changes?'),
|
|
764
|
+
show_choices=True,
|
|
765
|
+
default=YesOrNoResponse.YES.value,
|
|
766
|
+
type=click.Choice([r.value for r in YesOrNoResponse]),
|
|
767
|
+
show_default=True,
|
|
768
|
+
)
|
|
769
|
+
if response == YesOrNoResponse.NO.value:
|
|
770
|
+
typer.echo(__('Cannot checkout with uncommitted changes'))
|
|
771
|
+
raise typer.Exit(0)
|
|
772
|
+
|
|
773
|
+
commit_name = typer.prompt(
|
|
774
|
+
text=__('Please provide commit message'),
|
|
775
|
+
show_choices=False,
|
|
776
|
+
type=str,
|
|
777
|
+
show_default=False,
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
if commit_name:
|
|
781
|
+
commit_result = self.__git_local_client.commit_local_changes(
|
|
782
|
+
path=config.runtime.working_directory,
|
|
783
|
+
name=commit_name
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
if commit_result:
|
|
787
|
+
# in docs not Commit object, on real - str
|
|
788
|
+
if isinstance(commit_result, str):
|
|
789
|
+
typer.echo(commit_result)
|
|
790
|
+
|
|
791
|
+
self.__git_local_client.push_changes(
|
|
792
|
+
path=config.runtime.working_directory,
|
|
793
|
+
deploy_key_path=project_config.deploy_key_path
|
|
794
|
+
)
|
|
795
|
+
typer.echo(__("Pushed changes to remote repository"))
|
|
796
|
+
else:
|
|
797
|
+
typer.echo(__('Cannot commit with empty commit name'))
|
|
798
|
+
raise typer.Exit(0)
|
|
799
|
+
|
|
800
|
+
if target_commit_hash:
|
|
801
|
+
if self.__git_local_client.get_current_commit(path=config.runtime.working_directory).hexsha != target_commit_hash:
|
|
802
|
+
is_checkout_successful = self.__git_local_client.git_checkout_to_commit(
|
|
803
|
+
path=config.runtime.working_directory,
|
|
804
|
+
commit_hash=target_commit_hash
|
|
805
|
+
)
|
|
806
|
+
|
|
807
|
+
if is_checkout_successful:
|
|
808
|
+
print(f"Checked out to commit {target_commit_hash}")
|
|
809
|
+
print(f"[{ColorScheme.GIT_HEADLESS.value}]HEAD is detached. To be able make changes in repository, checkout to any branch.[/{ColorScheme.GIT_HEADLESS.value}]")
|
|
810
|
+
else:
|
|
811
|
+
typer.echo("HEAD is already at requested commit")
|
|
812
|
+
elif branch_name:
|
|
813
|
+
if self.__git_local_client.is_branch_exists(path=config.runtime.working_directory, branch_name=branch_name):
|
|
814
|
+
self.__git_local_client.git_checkout_to_branch(
|
|
815
|
+
path=config.runtime.working_directory,
|
|
816
|
+
branch=branch_name
|
|
817
|
+
)
|
|
818
|
+
typer.echo(f"Checked out to branch '{branch_name}'")
|
|
819
|
+
else:
|
|
820
|
+
typer.echo(f"Branch '{branch_name}' was not found in project repository")
|
|
821
|
+
else:
|
|
822
|
+
main_branch = self.__git_local_client.find_main_branch_name(path=config.runtime.working_directory)
|
|
823
|
+
if main_branch:
|
|
824
|
+
self.__git_local_client.git_checkout_to_branch(
|
|
825
|
+
path=config.runtime.working_directory,
|
|
826
|
+
branch=main_branch
|
|
827
|
+
)
|
|
828
|
+
typer.echo(f"Checked out to detected main branch: '{main_branch}'")
|
|
829
|
+
else:
|
|
830
|
+
typer.echo("No main branch found")
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
@error_handler()
|
|
836
|
+
def set_default_container(self, container_uid: Optional[str]):
|
|
837
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
838
|
+
|
|
839
|
+
if project_config is None:
|
|
840
|
+
typer.echo(f"No project found in working directory")
|
|
841
|
+
raise typer.Exit(1)
|
|
842
|
+
|
|
843
|
+
if container_uid:
|
|
844
|
+
container: DockerContainerDto = self.__thestage_api_client.get_container(
|
|
845
|
+
container_slug=container_uid,
|
|
846
|
+
)
|
|
847
|
+
if container is None:
|
|
848
|
+
typer.echo(f"Could not find container '{container_uid}'")
|
|
849
|
+
raise typer.Exit(1)
|
|
850
|
+
|
|
851
|
+
if container.project_id != project_config.id:
|
|
852
|
+
typer.echo(f"Provided container '{container_uid}' is not related to project '{project_config.slug}'")
|
|
853
|
+
raise typer.Exit(1)
|
|
854
|
+
|
|
855
|
+
if container.frontend_status.status_key != DockerContainerStatus.RUNNING:
|
|
856
|
+
typer.echo(f"Note: provided container '{container_uid}' is in status '{container.frontend_status.status_translation}'")
|
|
857
|
+
|
|
858
|
+
project_config.default_container_uid = container_uid
|
|
859
|
+
project_config.prompt_for_default_container = False
|
|
860
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
861
|
+
typer.echo("Default container settings were updated")
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
@error_handler()
|
|
865
|
+
def unset_default_container(self):
|
|
866
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
867
|
+
|
|
868
|
+
if project_config is None:
|
|
869
|
+
typer.echo(f"No project found in working directory")
|
|
870
|
+
raise typer.Exit(1)
|
|
871
|
+
|
|
872
|
+
project_config.default_container_uid = None
|
|
873
|
+
project_config.prompt_for_default_container = True # True or False?
|
|
874
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
875
|
+
typer.echo("Default container settings were updated")
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
@error_handler()
|
|
879
|
+
def print_project_config(self):
|
|
880
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
881
|
+
|
|
882
|
+
if project_config is None:
|
|
883
|
+
typer.echo(f"No project found in working directory")
|
|
884
|
+
raise typer.Exit(1)
|
|
885
|
+
|
|
886
|
+
is_deploy_key_exists = project_config.deploy_key_path and self.__file_system_service.check_if_path_exist(project_config.deploy_key_path)
|
|
887
|
+
|
|
888
|
+
typer.echo(tabulate(
|
|
889
|
+
[
|
|
890
|
+
[
|
|
891
|
+
"Project unique ID", project_config.slug
|
|
892
|
+
],
|
|
893
|
+
[
|
|
894
|
+
"Default docker container unique ID", project_config.default_container_uid if project_config.default_container_uid else "<None>"
|
|
895
|
+
],
|
|
896
|
+
[
|
|
897
|
+
"Deploy key path", project_config.deploy_key_path if is_deploy_key_exists else "<None>"
|
|
898
|
+
],
|
|
899
|
+
],
|
|
900
|
+
showindex=False,
|
|
901
|
+
tablefmt="simple",
|
|
902
|
+
))
|
|
903
|
+
|
|
904
|
+
if is_deploy_key_exists:
|
|
905
|
+
typer.echo("")
|
|
906
|
+
typer.echo(f"You can insert the following text:")
|
|
907
|
+
print(f"[{ColorScheme.USEFUL_INFO.value}]GIT_SSH_COMMAND=\"ssh -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i {project_config.deploy_key_path}\"[{ColorScheme.USEFUL_INFO.value}]")
|
|
908
|
+
typer.echo(f"before any regular git command to manage your local Project repository directly")
|
|
909
|
+
|
|
910
|
+
@error_handler()
|
|
911
|
+
def __get_fixed_project_config(self) -> Optional[ProjectConfig]:
|
|
912
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
913
|
+
if project_config is None:
|
|
914
|
+
return None
|
|
915
|
+
|
|
916
|
+
if not Path(project_config.deploy_key_path).is_file():
|
|
917
|
+
deploy_ssh_key = self.__thestage_api_client.get_project_deploy_ssh_key(
|
|
918
|
+
slug=project_config.slug,
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
deploy_key_path = self.__config_provider.save_project_deploy_ssh_key(
|
|
922
|
+
deploy_ssh_key=deploy_ssh_key,
|
|
923
|
+
project_slug=project_config.slug,
|
|
924
|
+
project_id=project_config.id,
|
|
925
|
+
)
|
|
926
|
+
|
|
927
|
+
project_config.deploy_key_path = deploy_key_path
|
|
928
|
+
self.__config_provider.save_project_config(project_config=project_config)
|
|
929
|
+
typer.echo(f'Recreated missing deploy key for the project')
|
|
930
|
+
|
|
931
|
+
return project_config
|
|
932
|
+
|
|
933
|
+
@error_handler()
|
|
934
|
+
def project_deploy_inference_simulator_model_to_instance(
|
|
935
|
+
self,
|
|
936
|
+
unique_id: Optional[str] = None,
|
|
937
|
+
unique_id_with_timestamp: Optional[str] = None,
|
|
938
|
+
rented_instance_unique_id: Optional[str] = None,
|
|
939
|
+
self_hosted_instance_unique_id: Optional[str] = None,
|
|
940
|
+
) -> None:
|
|
941
|
+
config = self.__config_provider.get_config()
|
|
942
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
943
|
+
if not project_config:
|
|
944
|
+
typer.echo(
|
|
945
|
+
__("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
|
|
946
|
+
{"path": config.runtime.working_directory}))
|
|
947
|
+
raise typer.Exit(1)
|
|
948
|
+
|
|
949
|
+
if rented_instance_unique_id and self_hosted_instance_unique_id:
|
|
950
|
+
typer.echo(__("Error: Cannot provide both rented and self-hosted instance unique IDs."))
|
|
951
|
+
raise typer.Exit(1)
|
|
952
|
+
|
|
953
|
+
if not rented_instance_unique_id and not self_hosted_instance_unique_id:
|
|
954
|
+
typer.echo(__("Error: Either a rented instance ID or a self-hosted instance unique ID must be provided."))
|
|
955
|
+
raise typer.Exit(1)
|
|
956
|
+
|
|
957
|
+
typer.echo(__("Creating inference simulator with unique ID: %unique_id_with_timestamp%", {"unique_id_with_timestamp": unique_id_with_timestamp}))
|
|
958
|
+
deploy_model_to_instance_response: DeployInferenceModelToInstanceResponse = self.__thestage_api_client.deploy_inference_model_to_instance(
|
|
959
|
+
unique_id=unique_id,
|
|
960
|
+
unique_id_with_timestamp=unique_id_with_timestamp,
|
|
961
|
+
rented_instance_unique_id=rented_instance_unique_id,
|
|
962
|
+
self_hosted_instance_unique_id=self_hosted_instance_unique_id
|
|
963
|
+
)
|
|
964
|
+
if deploy_model_to_instance_response:
|
|
965
|
+
if deploy_model_to_instance_response.message:
|
|
966
|
+
typer.echo(deploy_model_to_instance_response.message)
|
|
967
|
+
if deploy_model_to_instance_response.is_success:
|
|
968
|
+
typer.echo("Inference simulator has been scheduled to run successfully.")
|
|
969
|
+
else:
|
|
970
|
+
typer.echo(__(
|
|
971
|
+
'Inference simulator failed to run with an error: %server_massage%',
|
|
972
|
+
{'server_massage': deploy_model_to_instance_response.message or ""}
|
|
973
|
+
))
|
|
974
|
+
raise typer.Exit(1)
|
|
975
|
+
else:
|
|
976
|
+
typer.echo(__("Inference simulator failed to run with an error"))
|
|
977
|
+
raise typer.Exit(1)
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
@error_handler()
|
|
981
|
+
def project_deploy_inference_simulator_model_to_sagemaker(
|
|
982
|
+
self,
|
|
983
|
+
unique_id: Optional[str] = None,
|
|
984
|
+
arn: Optional[str] = None,
|
|
985
|
+
instance_type: Optional[str] = None,
|
|
986
|
+
initial_variant_weight: Optional[float] = 1.0,
|
|
987
|
+
initial_instance_count: Optional[int] = None,
|
|
988
|
+
) -> None:
|
|
989
|
+
config = self.__config_provider.get_config()
|
|
990
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
991
|
+
if not project_config:
|
|
992
|
+
typer.echo(
|
|
993
|
+
__("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
|
|
994
|
+
{"path": config.runtime.working_directory}))
|
|
995
|
+
raise typer.Exit(1)
|
|
996
|
+
|
|
997
|
+
if not instance_type:
|
|
998
|
+
typer.echo(__("Error: Instance type is required."))
|
|
999
|
+
raise typer.Exit(1)
|
|
1000
|
+
|
|
1001
|
+
if not initial_instance_count:
|
|
1002
|
+
typer.echo(__("Error: Initial instance count is required."))
|
|
1003
|
+
raise typer.Exit(1)
|
|
1004
|
+
|
|
1005
|
+
if not arn:
|
|
1006
|
+
typer.echo(__("Error: ARN is required."))
|
|
1007
|
+
raise typer.Exit(1)
|
|
1008
|
+
|
|
1009
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1010
|
+
if not project_config:
|
|
1011
|
+
typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.",
|
|
1012
|
+
{"path": config.runtime.working_directory}))
|
|
1013
|
+
raise typer.Exit(1)
|
|
1014
|
+
|
|
1015
|
+
deploy_model_to_sagemaker_response: DeployInferenceModelToSagemakerResponse = self.__thestage_api_client.deploy_inference_model_to_sagemaker(
|
|
1016
|
+
unique_id=unique_id,
|
|
1017
|
+
arn=arn,
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
if not deploy_model_to_sagemaker_response.is_success:
|
|
1021
|
+
typer.echo(__(
|
|
1022
|
+
'Failed to prepare model for deployment with an error: %server_massage%',
|
|
1023
|
+
{'server_massage': deploy_model_to_sagemaker_response.message or ""}
|
|
1024
|
+
))
|
|
1025
|
+
raise typer.Exit(1)
|
|
1026
|
+
|
|
1027
|
+
model_id = deploy_model_to_sagemaker_response.modelId
|
|
1028
|
+
image_uri = deploy_model_to_sagemaker_response.ecrImageUrl
|
|
1029
|
+
model_uri = deploy_model_to_sagemaker_response.s3ArtifactsUrl
|
|
1030
|
+
region = "us-east-1"
|
|
1031
|
+
sm_client = boto3.client('sagemaker', region_name=region)
|
|
1032
|
+
|
|
1033
|
+
try:
|
|
1034
|
+
container = {
|
|
1035
|
+
"Image": image_uri,
|
|
1036
|
+
"ModelDataUrl": model_uri,
|
|
1037
|
+
"Environment": {
|
|
1038
|
+
"SAGEMAKER_TRITON_DEFAULT_MODEL_NAME": model_id,
|
|
1039
|
+
"THESTAGE_API_URL": config.main.thestage_api_url,
|
|
1040
|
+
"THESTAGE_AUTH_TOKEN": config.main.thestage_auth_token
|
|
1041
|
+
},
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
sm_model_name = f"{unique_id}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
1045
|
+
create_model_response = sm_client.create_model(
|
|
1046
|
+
ModelName=sm_model_name,
|
|
1047
|
+
ExecutionRoleArn=arn,
|
|
1048
|
+
PrimaryContainer=container,
|
|
1049
|
+
)
|
|
1050
|
+
typer.echo(f"Model created successfully. Model ARN: {create_model_response['ModelArn']}")
|
|
1051
|
+
|
|
1052
|
+
endpoint_config_name = f"{unique_id}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
1053
|
+
create_endpoint_config_response = sm_client.create_endpoint_config(
|
|
1054
|
+
EndpointConfigName=endpoint_config_name,
|
|
1055
|
+
ProductionVariants=[
|
|
1056
|
+
{
|
|
1057
|
+
"InstanceType": instance_type,
|
|
1058
|
+
"InitialVariantWeight": initial_variant_weight,
|
|
1059
|
+
"InitialInstanceCount": initial_instance_count,
|
|
1060
|
+
"ModelName": sm_model_name,
|
|
1061
|
+
"VariantName": "AllTraffic",
|
|
1062
|
+
}
|
|
1063
|
+
],
|
|
1064
|
+
)
|
|
1065
|
+
typer.echo(
|
|
1066
|
+
f"Endpoint configuration created successfully. Endpoint Config ARN: {create_endpoint_config_response['EndpointConfigArn']}")
|
|
1067
|
+
|
|
1068
|
+
endpoint_name = f"{unique_id}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
1069
|
+
create_endpoint_response = sm_client.create_endpoint(
|
|
1070
|
+
EndpointName=endpoint_name,
|
|
1071
|
+
EndpointConfigName=endpoint_config_name,
|
|
1072
|
+
)
|
|
1073
|
+
typer.echo(f"Endpoint created successfully. Endpoint ARN: {create_endpoint_response['EndpointArn']}")
|
|
1074
|
+
|
|
1075
|
+
typer.echo("Waiting for the endpoint to become active...")
|
|
1076
|
+
while True:
|
|
1077
|
+
resp = sm_client.describe_endpoint(EndpointName=endpoint_name)
|
|
1078
|
+
status = resp["EndpointStatus"]
|
|
1079
|
+
typer.echo(f"Status: {status}")
|
|
1080
|
+
if status == "InService":
|
|
1081
|
+
break
|
|
1082
|
+
elif status == "Failed":
|
|
1083
|
+
typer.echo(f"Endpoint creation failed. Reason: {resp.get('FailureReason', 'Unknown')}")
|
|
1084
|
+
raise typer.Exit(1)
|
|
1085
|
+
time.sleep(60)
|
|
1086
|
+
|
|
1087
|
+
typer.echo(f"Endpoint is ready. ARN: {resp['EndpointArn']} Status: {status}")
|
|
1088
|
+
|
|
1089
|
+
except Exception as e:
|
|
1090
|
+
typer.echo(__("Failed to deploy the inference simulator model to SageMaker: %error%", {"error": str(e)}))
|
|
1091
|
+
raise typer.Exit(1)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
@error_handler()
|
|
1095
|
+
def pull_project(self):
|
|
1096
|
+
config = self.__config_provider.get_config()
|
|
1097
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
1098
|
+
if not project_config:
|
|
1099
|
+
typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.", {"path": config.runtime.working_directory}))
|
|
1100
|
+
raise typer.Exit(1)
|
|
1101
|
+
|
|
1102
|
+
typer.echo("Pulling code from remote repository...")
|
|
1103
|
+
self.__git_local_client.git_pull(
|
|
1104
|
+
path=config.runtime.working_directory,
|
|
1105
|
+
deploy_key_path=project_config.deploy_key_path,
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
@error_handler()
|
|
1110
|
+
def reset_project(self):
|
|
1111
|
+
config = self.__config_provider.get_config()
|
|
1112
|
+
project_config: ProjectConfig = self.__get_fixed_project_config()
|
|
1113
|
+
if not project_config:
|
|
1114
|
+
typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.", {"path": config.runtime.working_directory}))
|
|
1115
|
+
raise typer.Exit(1)
|
|
1116
|
+
|
|
1117
|
+
typer.echo("Fetching code from remote repository...")
|
|
1118
|
+
self.__git_local_client.git_fetch(
|
|
1119
|
+
path=config.runtime.working_directory,
|
|
1120
|
+
deploy_key_path=project_config.deploy_key_path,
|
|
1121
|
+
)
|
|
1122
|
+
typer.echo("Resetting local branch...")
|
|
1123
|
+
self.__git_local_client.reset_hard(
|
|
1124
|
+
path=config.runtime.working_directory,
|
|
1125
|
+
deploy_key_path=project_config.deploy_key_path,
|
|
1126
|
+
reset_to_origin=True
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
@error_handler()
|
|
1131
|
+
def print_inference_simulator_list(self, project_uid, statuses, row, page):
|
|
1132
|
+
if not project_uid:
|
|
1133
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1134
|
+
if not project_config:
|
|
1135
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1136
|
+
raise typer.Exit(1)
|
|
1137
|
+
project_uid = project_config.slug
|
|
1138
|
+
|
|
1139
|
+
inference_simulator_status_map = self.__thestage_api_client.get_inference_simulator_business_status_map()
|
|
1140
|
+
|
|
1141
|
+
if not statuses:
|
|
1142
|
+
statuses = ({key: inference_simulator_status_map[key] for key in [
|
|
1143
|
+
InferenceSimulatorStatus.SCHEDULED,
|
|
1144
|
+
InferenceSimulatorStatus.CREATING,
|
|
1145
|
+
InferenceSimulatorStatus.RUNNING,
|
|
1146
|
+
]}).values()
|
|
1147
|
+
|
|
1148
|
+
if "all" in statuses:
|
|
1149
|
+
statuses = inference_simulator_status_map.values()
|
|
1150
|
+
|
|
1151
|
+
for input_status_item in statuses:
|
|
1152
|
+
if input_status_item not in inference_simulator_status_map.values():
|
|
1153
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
1154
|
+
'invalid_status': input_status_item,
|
|
1155
|
+
'valid_statuses': str(list(inference_simulator_status_map.values()))
|
|
1156
|
+
}))
|
|
1157
|
+
raise typer.Exit(1)
|
|
1158
|
+
|
|
1159
|
+
typer.echo(__(
|
|
1160
|
+
"Listing inference simulators with the following statuses: %statuses%, to view all inference simulators, use --status all",
|
|
1161
|
+
placeholders={
|
|
1162
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
1163
|
+
}))
|
|
1164
|
+
|
|
1165
|
+
backend_statuses: List[str] = [key for key, value in inference_simulator_status_map.items() if value in statuses]
|
|
1166
|
+
|
|
1167
|
+
self.print(
|
|
1168
|
+
func_get_data=self.get_project_inference_simulator_list,
|
|
1169
|
+
func_special_params={
|
|
1170
|
+
'project_slug': project_uid,
|
|
1171
|
+
'statuses': backend_statuses,
|
|
1172
|
+
},
|
|
1173
|
+
mapper=ProjectInferenceSimulatorMapper(),
|
|
1174
|
+
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorEntity.model_fields.values())),
|
|
1175
|
+
row=row,
|
|
1176
|
+
page=page,
|
|
1177
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1178
|
+
show_index="never",
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
@error_handler()
|
|
1183
|
+
def print_inference_simulator_model_list(self, project_uid, statuses, row, page):
|
|
1184
|
+
if not project_uid:
|
|
1185
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1186
|
+
if not project_config:
|
|
1187
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1188
|
+
raise typer.Exit(1)
|
|
1189
|
+
project_uid = project_config.slug
|
|
1190
|
+
|
|
1191
|
+
inference_simulator_model_status_map = self.__thestage_api_client.get_inference_simulator_model_business_status_map()
|
|
1192
|
+
|
|
1193
|
+
if not statuses:
|
|
1194
|
+
statuses = ({key: inference_simulator_model_status_map[key] for key in [
|
|
1195
|
+
InferenceModelStatus.SCHEDULED,
|
|
1196
|
+
InferenceModelStatus.PROCESSING,
|
|
1197
|
+
InferenceModelStatus.PUSH_SUCCEED,
|
|
1198
|
+
]}).values()
|
|
1199
|
+
|
|
1200
|
+
if "all" in statuses:
|
|
1201
|
+
statuses = inference_simulator_model_status_map.values()
|
|
1202
|
+
|
|
1203
|
+
for input_status_item in statuses:
|
|
1204
|
+
if input_status_item not in inference_simulator_model_status_map.values():
|
|
1205
|
+
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
1206
|
+
'invalid_status': input_status_item,
|
|
1207
|
+
'valid_statuses': str(list(inference_simulator_model_status_map.values()))
|
|
1208
|
+
}))
|
|
1209
|
+
raise typer.Exit(1)
|
|
1210
|
+
|
|
1211
|
+
typer.echo(__(
|
|
1212
|
+
"Listing inference simulator models with the following statuses: %statuses%, to view all inference simulator models, use --status all",
|
|
1213
|
+
placeholders={
|
|
1214
|
+
'statuses': ', '.join([status_item for status_item in statuses])
|
|
1215
|
+
}))
|
|
1216
|
+
|
|
1217
|
+
backend_statuses: List[str] = [key for key, value in inference_simulator_model_status_map.items() if value in statuses]
|
|
1218
|
+
|
|
1219
|
+
self.print(
|
|
1220
|
+
func_get_data=self.get_project_inference_simulator_model_list,
|
|
1221
|
+
func_special_params={
|
|
1222
|
+
'project_slug': project_uid,
|
|
1223
|
+
'statuses': backend_statuses,
|
|
1224
|
+
},
|
|
1225
|
+
mapper=ProjectInferenceSimulatorModelMapper(),
|
|
1226
|
+
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorModelEntity.model_fields.values())),
|
|
1227
|
+
row=row,
|
|
1228
|
+
page=page,
|
|
1229
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1230
|
+
show_index="never",
|
|
1231
|
+
)
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
def print_task_list(self, project_uid, row, page):
|
|
1235
|
+
if not project_uid:
|
|
1236
|
+
project_config: ProjectConfig = self.__config_provider.read_project_config()
|
|
1237
|
+
if not project_config:
|
|
1238
|
+
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
1239
|
+
raise typer.Exit(1)
|
|
1240
|
+
project_uid = project_config.slug
|
|
1241
|
+
|
|
1242
|
+
self.print(
|
|
1243
|
+
func_get_data=self.get_project_task_list,
|
|
1244
|
+
func_special_params={
|
|
1245
|
+
'project_slug': project_uid,
|
|
1246
|
+
},
|
|
1247
|
+
mapper=ProjectTaskMapper(),
|
|
1248
|
+
headers=list(map(lambda x: x.alias, ProjectTaskEntity.model_fields.values())),
|
|
1249
|
+
row=row,
|
|
1250
|
+
page=page,
|
|
1251
|
+
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
1252
|
+
show_index="never",
|
|
1253
|
+
)
|