thestage 0.5.43__py3-none-any.whl → 0.5.45__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/__init__.py +1 -1
- thestage/color_scheme/color_scheme.py +2 -1
- thestage/controllers/container_controller.py +0 -3
- thestage/controllers/instance_controller.py +8 -75
- thestage/controllers/project_controller.py +92 -128
- thestage/main.py +6 -1
- thestage/services/clients/git/git_client.py +42 -24
- thestage/services/clients/thestage_api/api_client.py +16 -38
- thestage/services/clients/thestage_api/dtos/{user_profile.py → user_controller/user_profile.py} +6 -3
- thestage/services/config_provider/config_provider.py +10 -29
- thestage/services/connect/connect_service.py +12 -5
- thestage/services/container/container_service.py +2 -2
- thestage/services/core_files/config_entity.py +2 -7
- thestage/services/filesystem_service.py +20 -2
- thestage/services/instance/instance_service.py +100 -6
- thestage/services/logging/logging_service.py +1 -1
- thestage/services/project/project_service.py +202 -8
- thestage/services/remote_server_service.py +2 -2
- thestage/services/service_factory.py +4 -4
- thestage/services/validation_service.py +0 -6
- {thestage-0.5.43.dist-info → thestage-0.5.45.dist-info}/METADATA +2 -2
- {thestage-0.5.43.dist-info → thestage-0.5.45.dist-info}/RECORD +25 -25
- {thestage-0.5.43.dist-info → thestage-0.5.45.dist-info}/LICENSE.txt +0 -0
- {thestage-0.5.43.dist-info → thestage-0.5.45.dist-info}/WHEEL +0 -0
- {thestage-0.5.43.dist-info → thestage-0.5.45.dist-info}/entry_points.txt +0 -0
thestage/__init__.py
CHANGED
|
@@ -3,13 +3,10 @@ import re
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Optional, List
|
|
5
5
|
|
|
6
|
-
from thestage.entities.container import DockerContainerEntity
|
|
7
6
|
from thestage.services.clients.thestage_api.dtos.enums.container_pending_action import DockerContainerAction
|
|
8
7
|
from thestage.services.clients.thestage_api.dtos.container_response import DockerContainerDto
|
|
9
8
|
from thestage.i18n.translation import __
|
|
10
|
-
from thestage.services.clients.thestage_api.dtos.enums.container_status import DockerContainerStatus
|
|
11
9
|
from thestage.services.container.container_service import ContainerService
|
|
12
|
-
from thestage.services.container.mapper.container_mapper import ContainerMapper
|
|
13
10
|
from thestage.helpers.logger.app_logger import app_logger
|
|
14
11
|
from thestage.controllers.utils_controller import validate_config_and_get_service_factory, get_current_directory
|
|
15
12
|
|
|
@@ -57,46 +57,12 @@ def rented_list(
|
|
|
57
57
|
config = service_factory.get_config_provider().get_full_config()
|
|
58
58
|
|
|
59
59
|
instance_service: InstanceService = service_factory.get_instance_service()
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if not statuses:
|
|
63
|
-
statuses = ({key: instance_rented_status_map[key] for key in [
|
|
64
|
-
InstanceRentedBusinessStatus.ONLINE,
|
|
65
|
-
InstanceRentedBusinessStatus.CREATING,
|
|
66
|
-
InstanceRentedBusinessStatus.TERMINATING,
|
|
67
|
-
InstanceRentedBusinessStatus.REBOOTING,
|
|
68
|
-
]}).values()
|
|
69
|
-
|
|
70
|
-
if "all" in statuses:
|
|
71
|
-
statuses = instance_rented_status_map.values()
|
|
72
|
-
|
|
73
|
-
for input_status_item in statuses:
|
|
74
|
-
if input_status_item not in instance_rented_status_map.values():
|
|
75
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
76
|
-
'invalid_status': input_status_item,
|
|
77
|
-
'valid_statuses': str(list(instance_rented_status_map.values()))
|
|
78
|
-
}))
|
|
79
|
-
raise typer.Exit(1)
|
|
80
|
-
|
|
81
|
-
typer.echo(__(
|
|
82
|
-
"Listing rented server instances with the following statuses: %statuses%, to view all rented server instances, use --status all",
|
|
83
|
-
placeholders={
|
|
84
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
85
|
-
}))
|
|
86
|
-
|
|
87
|
-
backend_statuses: List[str] = [key for key, value in instance_rented_status_map.items() if value in statuses]
|
|
88
|
-
|
|
89
|
-
instance_service.print(
|
|
90
|
-
func_get_data=instance_service.get_rented_list,
|
|
91
|
-
func_special_params={
|
|
92
|
-
'statuses': backend_statuses,
|
|
93
|
-
},
|
|
94
|
-
mapper=InstanceMapper(),
|
|
60
|
+
|
|
61
|
+
instance_service.print_rented_instance_list(
|
|
95
62
|
config=config,
|
|
96
|
-
|
|
63
|
+
statuses=statuses,
|
|
97
64
|
row=row,
|
|
98
|
-
page=page
|
|
99
|
-
show_index="never",
|
|
65
|
+
page=page
|
|
100
66
|
)
|
|
101
67
|
|
|
102
68
|
typer.echo(__("Rented server instances listing complete"))
|
|
@@ -175,45 +141,12 @@ def self_hosted_list(
|
|
|
175
141
|
config = service_factory.get_config_provider().get_full_config()
|
|
176
142
|
|
|
177
143
|
instance_service: InstanceService = service_factory.get_instance_service()
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if not statuses:
|
|
181
|
-
statuses = ({key: selfhosted_instance_status_map[key] for key in [
|
|
182
|
-
SelfhostedBusinessStatus.AWAITING_CONFIGURATION,
|
|
183
|
-
SelfhostedBusinessStatus.RUNNING,
|
|
184
|
-
SelfhostedBusinessStatus.TERMINATED,
|
|
185
|
-
]}).values()
|
|
186
|
-
|
|
187
|
-
if "all" in statuses:
|
|
188
|
-
statuses = selfhosted_instance_status_map.values()
|
|
189
|
-
|
|
190
|
-
for input_status_item in statuses:
|
|
191
|
-
if input_status_item not in selfhosted_instance_status_map.values():
|
|
192
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
193
|
-
'invalid_status': input_status_item,
|
|
194
|
-
'valid_statuses': str(list(selfhosted_instance_status_map.values()))
|
|
195
|
-
}))
|
|
196
|
-
raise typer.Exit(1)
|
|
197
|
-
|
|
198
|
-
typer.echo(__(
|
|
199
|
-
"Listing self-hosted instances with the following statuses: %statuses%, to view all self-hosted instances, use --status all",
|
|
200
|
-
placeholders={
|
|
201
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
202
|
-
}))
|
|
203
|
-
|
|
204
|
-
backend_statuses: List[str] = [key for key, value in selfhosted_instance_status_map.items() if value in statuses]
|
|
205
|
-
|
|
206
|
-
instance_service.print(
|
|
207
|
-
func_get_data=instance_service.get_self_hosted_list,
|
|
208
|
-
func_special_params={
|
|
209
|
-
'statuses': backend_statuses,
|
|
210
|
-
},
|
|
211
|
-
mapper=SelfHostedMapper(),
|
|
144
|
+
|
|
145
|
+
instance_service.print_self_hosted_instance_list(
|
|
212
146
|
config=config,
|
|
213
|
-
|
|
147
|
+
statuses=statuses,
|
|
214
148
|
row=row,
|
|
215
|
-
page=page
|
|
216
|
-
show_index="never",
|
|
149
|
+
page=page
|
|
217
150
|
)
|
|
218
151
|
|
|
219
152
|
typer.echo(__("Self-hosted instances listing complete"))
|
|
@@ -183,6 +183,34 @@ def run(
|
|
|
183
183
|
raise typer.Exit(0)
|
|
184
184
|
|
|
185
185
|
|
|
186
|
+
@task_app.command(name='cancel', no_args_is_help=True, help=__("Cancel a task by ID"))
|
|
187
|
+
def cancel_task(
|
|
188
|
+
task_id: Annotated[int, typer.Argument(
|
|
189
|
+
help=__("Task ID (required)"),
|
|
190
|
+
)],
|
|
191
|
+
):
|
|
192
|
+
"""
|
|
193
|
+
Cancels a task
|
|
194
|
+
"""
|
|
195
|
+
app_logger.info(f'Start cancel task from {get_current_directory()}')
|
|
196
|
+
|
|
197
|
+
if not task_id:
|
|
198
|
+
typer.echo(__('Task ID is required'))
|
|
199
|
+
raise typer.Exit(1)
|
|
200
|
+
|
|
201
|
+
service_factory = validate_config_and_get_service_factory()
|
|
202
|
+
config = service_factory.get_config_provider().get_full_config()
|
|
203
|
+
|
|
204
|
+
project_service = service_factory.get_project_service()
|
|
205
|
+
|
|
206
|
+
project_service.cancel_task(
|
|
207
|
+
config=config,
|
|
208
|
+
task_id=task_id
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
raise typer.Exit(0)
|
|
212
|
+
|
|
213
|
+
|
|
186
214
|
@task_app.command("ls", help=__("List tasks"))
|
|
187
215
|
def list_runs(
|
|
188
216
|
project_uid: Annotated[str, typer.Argument(help=__("Project unique ID. By default, project info is taken from the current directory"), metavar="OPTIONAL")] = None,
|
|
@@ -208,29 +236,9 @@ def list_runs(
|
|
|
208
236
|
|
|
209
237
|
service_factory = validate_config_and_get_service_factory()
|
|
210
238
|
config = service_factory.get_config_provider().get_full_config()
|
|
211
|
-
|
|
212
239
|
project_service: ProjectService = service_factory.get_project_service()
|
|
213
240
|
|
|
214
|
-
|
|
215
|
-
project_config: ProjectConfig = service_factory.get_config_provider().read_project_config()
|
|
216
|
-
if not project_config:
|
|
217
|
-
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
218
|
-
raise typer.Exit(1)
|
|
219
|
-
project_uid = project_config.slug
|
|
220
|
-
|
|
221
|
-
project_service.print(
|
|
222
|
-
func_get_data=project_service.get_project_task_list,
|
|
223
|
-
func_special_params={
|
|
224
|
-
'project_slug': project_uid,
|
|
225
|
-
},
|
|
226
|
-
mapper=ProjectTaskMapper(),
|
|
227
|
-
config=config,
|
|
228
|
-
headers=list(map(lambda x: x.alias, ProjectTaskEntity.model_fields.values())),
|
|
229
|
-
row=row,
|
|
230
|
-
page=page,
|
|
231
|
-
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
232
|
-
show_index="never",
|
|
233
|
-
)
|
|
241
|
+
project_service.print_task_list(config, project_uid, row, page)
|
|
234
242
|
|
|
235
243
|
typer.echo(__("Tasks listing complete"))
|
|
236
244
|
raise typer.Exit(0)
|
|
@@ -287,9 +295,9 @@ def checkout_project(
|
|
|
287
295
|
),
|
|
288
296
|
):
|
|
289
297
|
"""
|
|
290
|
-
|
|
298
|
+
Checkout project in current working directory
|
|
291
299
|
"""
|
|
292
|
-
app_logger.info(f'Start project
|
|
300
|
+
app_logger.info(f'Start project checkout from {get_current_directory()}')
|
|
293
301
|
|
|
294
302
|
service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
|
|
295
303
|
config = service_factory.get_config_provider().get_full_config()
|
|
@@ -315,11 +323,8 @@ def checkout_project(
|
|
|
315
323
|
raise typer.Exit(0)
|
|
316
324
|
|
|
317
325
|
|
|
318
|
-
@
|
|
319
|
-
def
|
|
320
|
-
container_uid: Annotated[Optional[str], typer.Argument(
|
|
321
|
-
help=__("Unique ID of the container to use by default for running tasks"),
|
|
322
|
-
)] = None,
|
|
326
|
+
@app.command(name='pull', help=__("Pulls the changes from the remote project repository. Equivalent to 'git pull'."))
|
|
327
|
+
def pull_project(
|
|
323
328
|
working_directory: Optional[str] = typer.Option(
|
|
324
329
|
None,
|
|
325
330
|
"--working-directory",
|
|
@@ -327,6 +332,56 @@ def set_default_container(
|
|
|
327
332
|
help=__("Full path to working directory"),
|
|
328
333
|
is_eager=False,
|
|
329
334
|
),
|
|
335
|
+
):
|
|
336
|
+
"""
|
|
337
|
+
Pull project in current working directory
|
|
338
|
+
"""
|
|
339
|
+
app_logger.info(f'Start project pull from {get_current_directory()}')
|
|
340
|
+
|
|
341
|
+
service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
|
|
342
|
+
config = service_factory.get_config_provider().get_full_config()
|
|
343
|
+
|
|
344
|
+
project_service = service_factory.get_project_service()
|
|
345
|
+
|
|
346
|
+
project_service.pull_project(
|
|
347
|
+
config=config,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
raise typer.Exit(0)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@app.command(name='reset', help=__("Resets the current project branch to remote counterpart. All working tree changes will be lost. Equivalent to 'git fetch && git reset --hard origin/{ref}'."))
|
|
354
|
+
def reset_project(
|
|
355
|
+
working_directory: Optional[str] = typer.Option(
|
|
356
|
+
None,
|
|
357
|
+
"--working-directory",
|
|
358
|
+
"-wd",
|
|
359
|
+
help=__("Full path to working directory"),
|
|
360
|
+
is_eager=False,
|
|
361
|
+
),
|
|
362
|
+
):
|
|
363
|
+
"""
|
|
364
|
+
Pull project in current working directory
|
|
365
|
+
"""
|
|
366
|
+
app_logger.info(f'Start project pull from {get_current_directory()}')
|
|
367
|
+
|
|
368
|
+
service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
|
|
369
|
+
config = service_factory.get_config_provider().get_full_config()
|
|
370
|
+
|
|
371
|
+
project_service = service_factory.get_project_service()
|
|
372
|
+
|
|
373
|
+
project_service.reset_project(
|
|
374
|
+
config=config
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
raise typer.Exit(0)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
@config_app.command(name='set-default-container', no_args_is_help=True, help=__("Set default docker container for a project installation"))
|
|
381
|
+
def set_default_container(
|
|
382
|
+
container_uid: Annotated[Optional[str], typer.Argument(
|
|
383
|
+
help=__("Unique ID of the container to use by default for running tasks"),
|
|
384
|
+
)] = None,
|
|
330
385
|
unset_default_container: Optional[bool] = typer.Option(
|
|
331
386
|
False,
|
|
332
387
|
"--unset",
|
|
@@ -334,6 +389,13 @@ def set_default_container(
|
|
|
334
389
|
help=__("Unsets the default docker container"),
|
|
335
390
|
is_eager=False,
|
|
336
391
|
),
|
|
392
|
+
working_directory: Optional[str] = typer.Option(
|
|
393
|
+
None,
|
|
394
|
+
"--working-directory",
|
|
395
|
+
"-wd",
|
|
396
|
+
help=__("Full path to working directory"),
|
|
397
|
+
is_eager=False,
|
|
398
|
+
),
|
|
337
399
|
):
|
|
338
400
|
"""
|
|
339
401
|
Initializes project in current working directory
|
|
@@ -582,58 +644,9 @@ def list_inference_simulators(
|
|
|
582
644
|
|
|
583
645
|
service_factory = validate_config_and_get_service_factory()
|
|
584
646
|
config = service_factory.get_config_provider().get_full_config()
|
|
585
|
-
|
|
586
647
|
project_service: ProjectService = service_factory.get_project_service()
|
|
587
|
-
if not project_uid:
|
|
588
|
-
project_config: ProjectConfig = service_factory.get_config_provider().read_project_config()
|
|
589
|
-
if not project_config:
|
|
590
|
-
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
591
|
-
raise typer.Exit(1)
|
|
592
|
-
project_uid = project_config.slug
|
|
593
|
-
|
|
594
|
-
inference_simulator_status_map = service_factory.get_thestage_api_client().get_inference_simulator_business_status_map(
|
|
595
|
-
config.main.thestage_auth_token)
|
|
596
|
-
|
|
597
|
-
if not statuses:
|
|
598
|
-
statuses = ({key: inference_simulator_status_map[key] for key in [
|
|
599
|
-
InferenceSimulatorStatus.SCHEDULED,
|
|
600
|
-
InferenceSimulatorStatus.CREATING,
|
|
601
|
-
InferenceSimulatorStatus.RUNNING,
|
|
602
|
-
]}).values()
|
|
603
|
-
|
|
604
|
-
if "all" in statuses:
|
|
605
|
-
statuses = inference_simulator_status_map.values()
|
|
606
|
-
|
|
607
|
-
for input_status_item in statuses:
|
|
608
|
-
if input_status_item not in inference_simulator_status_map.values():
|
|
609
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
610
|
-
'invalid_status': input_status_item,
|
|
611
|
-
'valid_statuses': str(list(inference_simulator_status_map.values()))
|
|
612
|
-
}))
|
|
613
|
-
raise typer.Exit(1)
|
|
614
648
|
|
|
615
|
-
|
|
616
|
-
"Listing inference simulators with the following statuses: %statuses%, to view all inference simulators, use --status all",
|
|
617
|
-
placeholders={
|
|
618
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
619
|
-
}))
|
|
620
|
-
|
|
621
|
-
backend_statuses: List[str] = [key for key, value in inference_simulator_status_map.items() if value in statuses]
|
|
622
|
-
|
|
623
|
-
project_service.print(
|
|
624
|
-
func_get_data=project_service.get_project_inference_simulator_list,
|
|
625
|
-
func_special_params={
|
|
626
|
-
'project_slug': project_uid,
|
|
627
|
-
'statuses': backend_statuses,
|
|
628
|
-
},
|
|
629
|
-
mapper=ProjectInferenceSimulatorMapper(),
|
|
630
|
-
config=config,
|
|
631
|
-
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorEntity.model_fields.values())),
|
|
632
|
-
row=row,
|
|
633
|
-
page=page,
|
|
634
|
-
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
635
|
-
show_index="never",
|
|
636
|
-
)
|
|
649
|
+
project_service.print_inference_simulator_list(config, project_uid, statuses, row, page)
|
|
637
650
|
|
|
638
651
|
typer.echo(__("Inference simulators listing complete"))
|
|
639
652
|
raise typer.Exit(0)
|
|
@@ -671,58 +684,9 @@ def list_inference_simulator_models(
|
|
|
671
684
|
|
|
672
685
|
service_factory = validate_config_and_get_service_factory()
|
|
673
686
|
config = service_factory.get_config_provider().get_full_config()
|
|
674
|
-
|
|
675
687
|
project_service: ProjectService = service_factory.get_project_service()
|
|
676
|
-
if not project_uid:
|
|
677
|
-
project_config: ProjectConfig = service_factory.get_config_provider().read_project_config()
|
|
678
|
-
if not project_config:
|
|
679
|
-
typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
|
|
680
|
-
raise typer.Exit(1)
|
|
681
|
-
project_uid = project_config.slug
|
|
682
|
-
|
|
683
|
-
inference_simulator_model_status_map = service_factory.get_thestage_api_client().get_inference_simulator_model_business_status_map(
|
|
684
|
-
config.main.thestage_auth_token)
|
|
685
|
-
|
|
686
|
-
if not statuses:
|
|
687
|
-
statuses = ({key: inference_simulator_model_status_map[key] for key in [
|
|
688
|
-
InferenceModelStatus.SCHEDULED,
|
|
689
|
-
InferenceModelStatus.PROCESSING,
|
|
690
|
-
InferenceModelStatus.PUSH_SUCCEED,
|
|
691
|
-
]}).values()
|
|
692
|
-
|
|
693
|
-
if "all" in statuses:
|
|
694
|
-
statuses = inference_simulator_model_status_map.values()
|
|
695
|
-
|
|
696
|
-
for input_status_item in statuses:
|
|
697
|
-
if input_status_item not in inference_simulator_model_status_map.values():
|
|
698
|
-
typer.echo(__("'%invalid_status%' is not one of %valid_statuses%", {
|
|
699
|
-
'invalid_status': input_status_item,
|
|
700
|
-
'valid_statuses': str(list(inference_simulator_model_status_map.values()))
|
|
701
|
-
}))
|
|
702
|
-
raise typer.Exit(1)
|
|
703
688
|
|
|
704
|
-
|
|
705
|
-
"Listing inference simulator models with the following statuses: %statuses%, to view all inference simulator models, use --status all",
|
|
706
|
-
placeholders={
|
|
707
|
-
'statuses': ', '.join([status_item for status_item in statuses])
|
|
708
|
-
}))
|
|
709
|
-
|
|
710
|
-
backend_statuses: List[str] = [key for key, value in inference_simulator_model_status_map.items() if value in statuses]
|
|
711
|
-
|
|
712
|
-
project_service.print(
|
|
713
|
-
func_get_data=project_service.get_project_inference_simulator_model_list,
|
|
714
|
-
func_special_params={
|
|
715
|
-
'project_slug': project_uid,
|
|
716
|
-
'statuses': backend_statuses,
|
|
717
|
-
},
|
|
718
|
-
mapper=ProjectInferenceSimulatorModelMapper(),
|
|
719
|
-
config=config,
|
|
720
|
-
headers=list(map(lambda x: x.alias, ProjectInferenceSimulatorModelEntity.model_fields.values())),
|
|
721
|
-
row=row,
|
|
722
|
-
page=page,
|
|
723
|
-
max_col_width=[100, 100, 100, 100, 100, 100, 100, 100],
|
|
724
|
-
show_index="never",
|
|
725
|
-
)
|
|
689
|
+
project_service.print_inference_simulator_model_list(config, project_uid, statuses, row, page)
|
|
726
690
|
|
|
727
691
|
typer.echo(__("Inference simulator models listing complete"))
|
|
728
692
|
raise typer.Exit(0)
|
thestage/main.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from thestage.services.core_files.config_entity import ConfigEntity
|
|
2
|
+
|
|
3
|
+
# TODO use this in config_provider
|
|
4
|
+
initial_config: ConfigEntity = ConfigEntity()
|
|
5
|
+
|
|
1
6
|
def main():
|
|
2
7
|
try:
|
|
3
8
|
import warnings
|
|
@@ -8,9 +13,9 @@ def main():
|
|
|
8
13
|
from thestage.controllers import base_controller, container_controller, instance_controller, project_controller, \
|
|
9
14
|
config_controller
|
|
10
15
|
|
|
16
|
+
base_controller.app.add_typer(project_controller.app, name="project")
|
|
11
17
|
base_controller.app.add_typer(container_controller.app, name="container")
|
|
12
18
|
base_controller.app.add_typer(instance_controller.app, name="instance")
|
|
13
|
-
base_controller.app.add_typer(project_controller.app, name="project")
|
|
14
19
|
base_controller.app.add_typer(config_controller.app, name="config")
|
|
15
20
|
|
|
16
21
|
import thestage.config
|
|
@@ -10,15 +10,16 @@ from gitdb.exc import BadName
|
|
|
10
10
|
from rich import print
|
|
11
11
|
|
|
12
12
|
from thestage.color_scheme.color_scheme import ColorScheme
|
|
13
|
+
from thestage.config import THESTAGE_CONFIG_DIR
|
|
13
14
|
from thestage.exceptions.git_access_exception import GitAccessException
|
|
14
15
|
from thestage.git.ProgressPrinter import ProgressPrinter
|
|
15
|
-
from thestage.services.filesystem_service import
|
|
16
|
+
from thestage.services.filesystem_service import FileSystemService
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class GitLocalClient:
|
|
19
20
|
__base_name_remote: str = 'origin'
|
|
20
21
|
__base_name_local: str = 'main'
|
|
21
|
-
__git_ignore_thestage_line: str = '
|
|
22
|
+
__git_ignore_thestage_line: str = f'/{THESTAGE_CONFIG_DIR}/'
|
|
22
23
|
|
|
23
24
|
__special_main_branches = ['main', 'master']
|
|
24
25
|
|
|
@@ -26,10 +27,11 @@ class GitLocalClient:
|
|
|
26
27
|
|
|
27
28
|
def __init__(
|
|
28
29
|
self,
|
|
29
|
-
file_system_service:
|
|
30
|
+
file_system_service: FileSystemService,
|
|
30
31
|
):
|
|
31
32
|
self.__file_system_service = file_system_service
|
|
32
33
|
|
|
34
|
+
# todo delete this fuckery
|
|
33
35
|
def __get_repo(self, path: str) -> Repo:
|
|
34
36
|
return git.Repo(path)
|
|
35
37
|
|
|
@@ -105,32 +107,37 @@ class GitLocalClient:
|
|
|
105
107
|
with repo.git.custom_environment(GIT_SSH_COMMAND=git_ssh_cmd):
|
|
106
108
|
remote: Remote = repo.remote(self.__base_name_remote)
|
|
107
109
|
if remote:
|
|
110
|
+
progress = ProgressPrinter()
|
|
108
111
|
try:
|
|
109
|
-
remote.fetch()
|
|
110
|
-
except GitCommandError as
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
else:
|
|
119
|
-
raise base_ex
|
|
120
|
-
|
|
121
|
-
def git_pull(self, path: str, deploy_key_path: str, branch: Optional[str] = None):
|
|
112
|
+
remote.fetch(progress=progress)
|
|
113
|
+
except GitCommandError as ex:
|
|
114
|
+
for line in progress.allDroppedLines():
|
|
115
|
+
# returning the whole output if failed - so that user have any idea what's going on
|
|
116
|
+
print(f'>> {line}')
|
|
117
|
+
raise ex
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def git_pull(self, path: str, deploy_key_path: str):
|
|
122
121
|
repo = self.__get_repo(path=path)
|
|
123
122
|
git_ssh_cmd = 'ssh -F /dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i %s' % deploy_key_path
|
|
124
123
|
|
|
125
124
|
with repo.git.custom_environment(GIT_SSH_COMMAND=git_ssh_cmd):
|
|
126
125
|
local_branch = self.__base_name_local
|
|
127
|
-
if
|
|
128
|
-
|
|
129
|
-
local_branch = branch.replace(f"{self.__base_name_remote}/", '').strip()
|
|
130
|
-
else:
|
|
131
|
-
local_branch = branch
|
|
126
|
+
if repo.active_branch.name:
|
|
127
|
+
local_branch = repo.active_branch.name
|
|
132
128
|
|
|
133
|
-
repo.
|
|
129
|
+
origin = repo.remote(self.__base_name_remote)
|
|
130
|
+
|
|
131
|
+
if origin:
|
|
132
|
+
progress = ProgressPrinter()
|
|
133
|
+
try:
|
|
134
|
+
origin.pull(refspec=local_branch, progress=progress)
|
|
135
|
+
typer.echo(f"Pulled remote changes to branch '{local_branch}'")
|
|
136
|
+
except GitCommandError as ex:
|
|
137
|
+
for line in progress.allDroppedLines():
|
|
138
|
+
# returning the whole output if failed - so that user have any idea what's going on
|
|
139
|
+
print(f'>> {line}')
|
|
140
|
+
raise ex
|
|
134
141
|
|
|
135
142
|
def find_main_branch_name(self, path: str) -> Optional[str]:
|
|
136
143
|
repo = self.__get_repo(path=path)
|
|
@@ -275,10 +282,16 @@ class GitLocalClient:
|
|
|
275
282
|
if repo:
|
|
276
283
|
return repo.head.is_detached
|
|
277
284
|
|
|
278
|
-
def reset_hard(self, path: str):
|
|
285
|
+
def reset_hard(self, path: str, deploy_key_path: str, reset_to_origin: bool):
|
|
279
286
|
repo = self.__get_repo(path=path)
|
|
280
287
|
if repo:
|
|
281
|
-
|
|
288
|
+
git_ssh_cmd = 'ssh -F /dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i %s' % deploy_key_path
|
|
289
|
+
with repo.git.custom_environment(GIT_SSH_COMMAND=git_ssh_cmd):
|
|
290
|
+
if reset_to_origin:
|
|
291
|
+
repo.git.reset('--hard', f'origin/{repo.active_branch.name}')
|
|
292
|
+
typer.echo(f'Branch "{repo.active_branch.name}" is now synced to its remote counterpart')
|
|
293
|
+
else:
|
|
294
|
+
typer.echo('simple branch reset is not implemented')
|
|
282
295
|
|
|
283
296
|
# refers to a "headless commit" where something was committed while in detached head state and head is pointing at that commit
|
|
284
297
|
def is_head_committed_in_headless_state(self, path: str) -> bool:
|
|
@@ -297,6 +310,11 @@ class GitLocalClient:
|
|
|
297
310
|
for branch in repo.heads:
|
|
298
311
|
if branch.name == branch_name:
|
|
299
312
|
return True
|
|
313
|
+
|
|
314
|
+
for ref in repo.remotes.origin.refs:
|
|
315
|
+
if ref.remote_head == branch_name:
|
|
316
|
+
typer.echo(f'Found remote branch "{branch_name}"')
|
|
317
|
+
return True
|
|
300
318
|
return False
|
|
301
319
|
|
|
302
320
|
def checkout_to_new_branch(self, path: str, branch_name: str):
|