stoobly-agent 1.10.1__py3-none-any.whl → 1.10.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.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/__main__.py +10 -0
- stoobly_agent/app/cli/ca_cert_cli.py +9 -5
- stoobly_agent/app/cli/helpers/replay_facade.py +2 -2
- stoobly_agent/app/cli/intercept_cli.py +5 -5
- stoobly_agent/app/cli/request_cli.py +2 -2
- stoobly_agent/app/cli/scaffold/app.py +14 -5
- stoobly_agent/app/cli/scaffold/app_command.py +0 -4
- stoobly_agent/app/cli/scaffold/app_config.py +49 -2
- stoobly_agent/app/cli/scaffold/app_create_command.py +145 -76
- stoobly_agent/app/cli/scaffold/constants.py +8 -1
- stoobly_agent/app/cli/scaffold/docker/constants.py +3 -1
- stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +2 -2
- stoobly_agent/app/cli/scaffold/docker/service/builder.py +15 -49
- stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py +3 -0
- stoobly_agent/app/cli/scaffold/docker/template_files.py +112 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +31 -39
- stoobly_agent/app/cli/scaffold/docker/workflow/command_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/detached_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py +2 -3
- stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py +423 -0
- stoobly_agent/app/cli/scaffold/local/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/service/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/service/builder.py +72 -0
- stoobly_agent/app/cli/scaffold/local/workflow/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/workflow/builder.py +35 -0
- stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +339 -0
- stoobly_agent/app/cli/scaffold/service_command.py +9 -1
- stoobly_agent/app/cli/scaffold/service_config.py +8 -0
- stoobly_agent/app/cli/scaffold/service_create_command.py +18 -6
- stoobly_agent/app/cli/scaffold/service_docker_compose.py +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up +0 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/constants.py +35 -19
- stoobly_agent/app/cli/scaffold/templates/factory.py +34 -18
- stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run +21 -0
- stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run +21 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/run +3 -0
- stoobly_agent/app/cli/scaffold/workflow_command.py +18 -4
- stoobly_agent/app/cli/scaffold/workflow_copy_command.py +5 -4
- stoobly_agent/app/cli/scaffold/workflow_create_command.py +31 -29
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +18 -151
- stoobly_agent/app/cli/scaffold_cli.py +115 -161
- stoobly_agent/app/cli/scenario_cli.py +2 -2
- stoobly_agent/app/cli/types/test.py +2 -2
- stoobly_agent/app/cli/types/workflow_run_command.py +52 -3
- stoobly_agent/app/proxy/handle_mock_service.py +1 -1
- stoobly_agent/app/proxy/intercept_settings.py +5 -25
- stoobly_agent/app/proxy/mock/eval_fixtures_service.py +177 -27
- stoobly_agent/app/proxy/mock/types/__init__.py +22 -1
- stoobly_agent/app/proxy/replay/body_parser_service.py +8 -5
- stoobly_agent/app/proxy/replay/multipart.py +15 -13
- stoobly_agent/app/proxy/replay/replay_request_service.py +2 -2
- stoobly_agent/app/proxy/run.py +3 -0
- stoobly_agent/app/proxy/test/context.py +0 -4
- stoobly_agent/app/proxy/test/context_abc.py +0 -5
- stoobly_agent/cli.py +61 -16
- stoobly_agent/config/data_dir.py +0 -8
- stoobly_agent/public/12-es2015.618ecfd5f735b801b50f.js +1 -0
- stoobly_agent/public/12-es5.618ecfd5f735b801b50f.js +1 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/runtime-es2015.77bcd31efed9e5d5d431.js +1 -0
- stoobly_agent/public/runtime-es5.77bcd31efed9e5d5d431.js +1 -0
- stoobly_agent/test/app/cli/intercept/intercept_configure_test.py +17 -6
- stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py +177 -0
- stoobly_agent/test/app/cli/scaffold/{cli_test.py → docker/cli_test.py} +1 -8
- stoobly_agent/test/app/cli/scaffold/{e2e_test.py → docker/e2e_test.py} +31 -16
- stoobly_agent/test/app/cli/scaffold/local/__init__.py +0 -0
- stoobly_agent/test/app/cli/scaffold/{cli_invoker.py → local/cli_invoker.py} +38 -32
- stoobly_agent/test/app/cli/scaffold/local/e2e_test.py +342 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +903 -2
- stoobly_agent/test/app/proxy/replay/body_parser_service_test.py +95 -3
- stoobly_agent/test/config/data_dir_test.py +2 -7
- stoobly_agent/test/test_helper.py +16 -5
- {stoobly_agent-1.10.1.dist-info → stoobly_agent-1.10.2.dist-info}/METADATA +4 -2
- {stoobly_agent-1.10.1.dist-info → stoobly_agent-1.10.2.dist-info}/RECORD +150 -122
- {stoobly_agent-1.10.1.dist-info → stoobly_agent-1.10.2.dist-info}/WHEEL +1 -1
- stoobly_agent/app/cli/helpers/shell.py +0 -26
- stoobly_agent/public/12-es2015.be58ed0ef449008b932e.js +0 -1
- stoobly_agent/public/12-es5.be58ed0ef449008b932e.js +0 -1
- stoobly_agent/public/runtime-es2015.f8c814b38b27708e91c1.js +0 -1
- stoobly_agent/public/runtime-es5.f8c814b38b27708e91c1.js +0 -1
- /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/record/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/test/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/test/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/{.docker-compose.exec.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/mock/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/record/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/test/{bin/configure → configure} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/test/{bin/init → init} +0 -0
- {stoobly_agent-1.10.1.dist-info → stoobly_agent-1.10.2.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-1.10.1.dist-info → stoobly_agent-1.10.2.dist-info/licenses}/LICENSE +0 -0
@@ -3,20 +3,16 @@ import os
|
|
3
3
|
import pdb
|
4
4
|
import sys
|
5
5
|
|
6
|
-
from io import TextIOWrapper
|
7
|
-
from typing import List
|
8
|
-
from urllib.parse import urlparse
|
9
6
|
|
7
|
+
from stoobly_agent.app.cli.ca_cert_cli import ca_cert_install
|
10
8
|
from stoobly_agent.app.cli.helpers.certificate_authority import CertificateAuthority
|
11
|
-
from stoobly_agent.app.cli.helpers.shell import exec_stream
|
12
9
|
from stoobly_agent.app.cli.scaffold.app import App
|
10
|
+
from stoobly_agent.app.cli.scaffold.app_config import AppConfig
|
13
11
|
from stoobly_agent.app.cli.scaffold.app_create_command import AppCreateCommand
|
14
12
|
from stoobly_agent.app.cli.scaffold.constants import (
|
15
|
-
SERVICES_NAMESPACE, WORKFLOW_CONTAINER_PROXY, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
|
13
|
+
PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT, RUN_ON_DOCKER, RUN_ON_OPTIONS, SERVICES_NAMESPACE, WORKFLOW_CONTAINER_PROXY, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
|
16
14
|
)
|
17
|
-
from stoobly_agent.app.cli.scaffold.constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT
|
18
15
|
from stoobly_agent.app.cli.scaffold.containerized_app import ContainerizedApp
|
19
|
-
from stoobly_agent.app.cli.scaffold.docker.service.configure_gateway import configure_gateway
|
20
16
|
from stoobly_agent.app.cli.scaffold.docker.workflow.decorators_factory import get_workflow_decorators
|
21
17
|
from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
|
22
18
|
from stoobly_agent.app.cli.scaffold.service import Service
|
@@ -30,16 +26,16 @@ from stoobly_agent.app.cli.scaffold.validate_exceptions import ScaffoldValidateE
|
|
30
26
|
from stoobly_agent.app.cli.scaffold.workflow import Workflow
|
31
27
|
from stoobly_agent.app.cli.scaffold.workflow_create_command import WorkflowCreateCommand
|
32
28
|
from stoobly_agent.app.cli.scaffold.workflow_copy_command import WorkflowCopyCommand
|
33
|
-
from stoobly_agent.app.cli.scaffold.workflow_log_command import WorkflowLogCommand
|
34
29
|
from stoobly_agent.app.cli.scaffold.workflow_namesapce import WorkflowNamespace
|
35
|
-
from stoobly_agent.app.cli.scaffold.
|
30
|
+
from stoobly_agent.app.cli.scaffold.docker.workflow.run_command import DockerWorkflowRunCommand
|
31
|
+
from stoobly_agent.app.cli.scaffold.local.workflow.run_command import LocalWorkflowRunCommand
|
36
32
|
from stoobly_agent.app.cli.scaffold.workflow_validate_command import WorkflowValidateCommand
|
37
33
|
from stoobly_agent.config.constants import env_vars
|
38
34
|
from stoobly_agent.config.data_dir import DataDir
|
39
35
|
from stoobly_agent.lib.logger import bcolors, DEBUG, ERROR, INFO, Logger, WARNING
|
40
36
|
|
41
37
|
from .helpers.print_service import FORMATS, print_services, select_print_options
|
42
|
-
from .validators.scaffold import validate_app_name, validate_hostname, validate_namespace,
|
38
|
+
from .validators.scaffold import validate_app_name, validate_hostname, validate_namespace, validate_service_name
|
43
39
|
|
44
40
|
LOG_ID = 'Scaffold'
|
45
41
|
|
@@ -92,7 +88,9 @@ def hostname(ctx):
|
|
92
88
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to create the app scaffold.')
|
93
89
|
@click.option('--docker-socket-path', default='/var/run/docker.sock', type=click.Path(exists=True, file_okay=True, dir_okay=False), help='Path to Docker socket.')
|
94
90
|
@click.option('--plugin', multiple=True, type=click.Choice([PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT]), help='Scaffold integrations.')
|
91
|
+
@click.option('--proxy-port', default=8080, type=click.IntRange(1, 65535), help='Proxy service port.')
|
95
92
|
@click.option('--quiet', is_flag=True, help='Disable log output.')
|
93
|
+
@click.option('--run-on', multiple=True, type=click.Choice(RUN_ON_OPTIONS), default=[RUN_ON_DOCKER], help='Runtime environments to support (default: docker).')
|
96
94
|
@click.option('--ui-port', default=4200, type=click.IntRange(1, 65535), help='UI service port.')
|
97
95
|
@click.argument('app_name', callback=validate_app_name)
|
98
96
|
def create(**kwargs):
|
@@ -343,13 +341,10 @@ def copy(**kwargs):
|
|
343
341
|
@click.option('--context-dir-path', default=data_dir.context_dir_path, help='Path to Stoobly data directory.')
|
344
342
|
@click.option('--containerized', is_flag=True, help='Set if run from within a container.')
|
345
343
|
@click.option('--dry-run', default=False, is_flag=True)
|
346
|
-
@click.option('--extra-entrypoint-compose-path', help='Path to extra entrypoint compose file.')
|
347
344
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
348
345
|
Log levels can be "debug", "info", "warning", or "error"
|
349
346
|
''')
|
350
347
|
@click.option('--namespace', callback=validate_namespace, help='Workflow namespace.')
|
351
|
-
@click.option('--network', callback=validate_network, help='Workflow network name.')
|
352
|
-
@click.option('--rmi', is_flag=True, help='Remove images used by containers.')
|
353
348
|
@click.option('--script-path', help='Path to intermediate script path.')
|
354
349
|
@click.option('--service', multiple=True, help='Select which services to log. Defaults to all.')
|
355
350
|
@click.option('--user-id', default=os.getuid(), help='OS user ID of the owner of context dir path.')
|
@@ -364,59 +359,38 @@ def down(**kwargs):
|
|
364
359
|
__validate_app(app)
|
365
360
|
|
366
361
|
__with_namespace_defaults(kwargs)
|
367
|
-
__with_workflow_namespace(app, kwargs['namespace'])
|
368
362
|
|
369
363
|
services = __get_services(
|
370
364
|
app, service=kwargs['service'], workflow=[kwargs['workflow_name']]
|
371
365
|
)
|
372
366
|
|
373
|
-
|
374
|
-
for service in services:
|
375
|
-
config = { **kwargs }
|
376
|
-
config['service_name'] = service
|
377
|
-
command = WorkflowRunCommand(app, **config)
|
378
|
-
command.current_working_dir = current_working_dir
|
379
|
-
commands.append(command)
|
380
|
-
|
367
|
+
command_args = { 'print_service_header': lambda service_name: __print_header(f"Step {service_name}") }
|
381
368
|
script = __build_script(app, **kwargs)
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
extra_compose_path = kwargs['extra_entrypoint_compose_path']
|
393
|
-
|
394
|
-
exec_command = command.down(
|
395
|
-
extra_compose_path=extra_compose_path,
|
396
|
-
namespace=kwargs['namespace'],
|
397
|
-
rmi=kwargs['rmi'],
|
398
|
-
user_id=kwargs['user_id']
|
369
|
+
|
370
|
+
# Determine which workflow command to use based on app configuration
|
371
|
+
app_config = AppConfig(app.scaffold_namespace_path)
|
372
|
+
if app_config.run_on_local:
|
373
|
+
# Use LocalWorkflowRunCommand for local execution
|
374
|
+
workflow_command = LocalWorkflowRunCommand(
|
375
|
+
app,
|
376
|
+
services=services,
|
377
|
+
script=script,
|
378
|
+
**kwargs
|
399
379
|
)
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
print(remove_egress_network_command, file=script)
|
415
|
-
|
416
|
-
remove_ingress_network_command = command.remove_ingress_network()
|
417
|
-
print(remove_ingress_network_command, file=script)
|
418
|
-
|
419
|
-
__run_script(script, kwargs['dry_run'])
|
380
|
+
else:
|
381
|
+
# Use DockerWorkflowRunCommand for Docker execution
|
382
|
+
workflow_command = DockerWorkflowRunCommand(
|
383
|
+
app,
|
384
|
+
services=services,
|
385
|
+
script=script,
|
386
|
+
**kwargs
|
387
|
+
)
|
388
|
+
|
389
|
+
# Execute the workflow down
|
390
|
+
workflow_command.down(
|
391
|
+
**command_args,
|
392
|
+
**kwargs
|
393
|
+
)
|
420
394
|
|
421
395
|
# Options are no longer valid
|
422
396
|
if kwargs['containerized'] and os.path.exists(data_dir.mitmproxy_options_json_path):
|
@@ -451,37 +425,34 @@ def logs(**kwargs):
|
|
451
425
|
app, service=kwargs['service'], without_core=True, workflow=[kwargs['workflow_name']]
|
452
426
|
)
|
453
427
|
|
454
|
-
|
455
|
-
for service in services:
|
456
|
-
if len(kwargs['service']) == 0:
|
457
|
-
# If no filter is specified, ignore CORE_SERVICES
|
458
|
-
if service in CORE_SERVICES:
|
459
|
-
continue
|
460
|
-
else:
|
461
|
-
# If a filter is specified, ignore all other services
|
462
|
-
if service not in kwargs['service']:
|
463
|
-
continue
|
464
|
-
|
465
|
-
config = { **kwargs }
|
466
|
-
config['service_name'] = service
|
467
|
-
command = WorkflowLogCommand(app, **config)
|
468
|
-
commands.append(command)
|
469
|
-
|
428
|
+
command_args = { 'print_service_header': lambda service_name: __print_header(f"SERVICE {service_name}") }
|
470
429
|
script = __build_script(app, **kwargs)
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
430
|
+
|
431
|
+
# Determine which workflow command to use based on app configuration
|
432
|
+
app_config = AppConfig(app.scaffold_namespace_path)
|
433
|
+
if app_config.run_on_local:
|
434
|
+
# Use LocalWorkflowRunCommand for local execution
|
435
|
+
workflow_command = LocalWorkflowRunCommand(
|
436
|
+
app,
|
437
|
+
services=services,
|
438
|
+
script=script,
|
439
|
+
**kwargs
|
479
440
|
)
|
441
|
+
else:
|
442
|
+
# Use DockerWorkflowRunCommand for Docker execution
|
443
|
+
workflow_command = DockerWorkflowRunCommand(
|
444
|
+
app,
|
445
|
+
services=services,
|
446
|
+
script=script,
|
447
|
+
**kwargs
|
448
|
+
)
|
449
|
+
command_args['print_service_header'] = lambda service_name: __print_header(f"SERVICE {service_name}")
|
480
450
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
451
|
+
# Execute the workflow logs
|
452
|
+
workflow_command.logs(
|
453
|
+
**command_args,
|
454
|
+
**kwargs
|
455
|
+
)
|
485
456
|
|
486
457
|
@workflow.command()
|
487
458
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to application directory.')
|
@@ -489,28 +460,25 @@ def logs(**kwargs):
|
|
489
460
|
@click.option('--certs-dir-path', help='Path to certs directory. Defaults to the certs dir of the context.')
|
490
461
|
@click.option('--containerized', is_flag=True, help='Set if run from within a container.')
|
491
462
|
@click.option('--context-dir-path', default=data_dir.context_dir_path, help='Path to Stoobly data directory.')
|
492
|
-
@click.option('--detached', is_flag=True, help='If set, will
|
463
|
+
@click.option('--detached', is_flag=True, help='If set, will run the highest priority service in the background.')
|
493
464
|
@click.option('--dry-run', default=False, is_flag=True, help='If set, prints commands.')
|
494
|
-
@click.option('--extra-entrypoint-compose-path', help='Path to extra entrypoint compose file.')
|
495
465
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
496
466
|
Log levels can be "debug", "info", "warning", or "error"
|
497
467
|
''')
|
498
468
|
@click.option('--mkcert', is_flag=True, help='Set to generate SSL certs for HTTPS services.')
|
499
469
|
@click.option('--namespace', callback=validate_namespace, help='Workflow namespace.')
|
500
|
-
@click.option('--network', callback=validate_network, help='Workflow network name.')
|
501
|
-
@click.option('--no-build', is_flag=True, help='Do not build images before starting containers.')
|
502
470
|
@click.option('--no-publish', is_flag=True, help='Do not publish all ports.')
|
503
|
-
@click.option('--pull', is_flag=True, help='Pull image before running.')
|
504
471
|
@click.option('--script-path', help='Path to intermediate script path.')
|
505
472
|
@click.option('--service', multiple=True, help='Select which services to run. Defaults to all.')
|
506
473
|
@click.option('--user-id', default=os.getuid(), help='OS user ID of the owner of context dir path.')
|
507
474
|
@click.option('--verbose', is_flag=True)
|
508
|
-
@click.option('
|
475
|
+
@click.option('-y', '--yes', is_flag=True, help='Auto-confirm CA certificate installation prompt.')
|
509
476
|
@click.argument('workflow_name')
|
510
477
|
def up(**kwargs):
|
511
478
|
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
512
479
|
|
513
480
|
containerized = kwargs['containerized']
|
481
|
+
dry_run = kwargs['dry_run']
|
514
482
|
|
515
483
|
# Because we are running a docker-compose command which depends on APP_DIR env var
|
516
484
|
# when we are running this command within a container, the host's app_dir_path will likely differ
|
@@ -519,76 +487,72 @@ def up(**kwargs):
|
|
519
487
|
__validate_app(app)
|
520
488
|
|
521
489
|
__with_namespace_defaults(kwargs)
|
522
|
-
|
490
|
+
|
491
|
+
# First time if folder does not exist or is empty
|
492
|
+
first_time = not os.path.exists(app.ca_certs_dir_path) or not os.listdir(app.ca_certs_dir_path)
|
493
|
+
if first_time and not containerized and not dry_run:
|
494
|
+
# If ca certs dir path does not exist, run ca-cert install
|
495
|
+
if kwargs.get('yes'):
|
496
|
+
# Auto-confirm if -y/--yes option is provided
|
497
|
+
ca_cert_install(app.ca_certs_dir_path)
|
498
|
+
else:
|
499
|
+
confirm = input(f"Installing CA certificate is required for {kwargs['workflow_name']}ing requests, continue? (y/N) ")
|
500
|
+
if confirm == "y" or confirm == "Y":
|
501
|
+
ca_cert_install(app.ca_certs_dir_path)
|
502
|
+
else:
|
503
|
+
print("You can install the CA certificate later by running: stoobly-agent ca-cert install")
|
504
|
+
sys.exit(1)
|
523
505
|
|
524
506
|
services = __get_services(
|
525
507
|
app, service=kwargs['service'], workflow=[kwargs['workflow_name']]
|
526
508
|
)
|
527
509
|
|
528
|
-
|
529
|
-
_app = ContainerizedApp(app_dir_path, SERVICES_NAMESPACE) if containerized else app
|
530
|
-
__services_mkcert(_app, services)
|
531
|
-
|
532
|
-
# Gateway ports are dynamically set depending on the workflow run
|
533
|
-
workflow = Workflow(kwargs['workflow_name'], app)
|
534
|
-
configure_gateway(workflow_namespace, workflow.service_paths_from_services(services), kwargs['no_publish'])
|
535
|
-
|
536
|
-
commands: List[WorkflowRunCommand] = []
|
537
|
-
for service in services:
|
538
|
-
config = { **kwargs }
|
539
|
-
config['service_name'] = service
|
540
|
-
command = WorkflowRunCommand(app, **config)
|
541
|
-
command.current_working_dir = current_working_dir
|
542
|
-
commands.append(command)
|
543
|
-
|
510
|
+
command_args = { 'print_service_header': lambda service_name: __print_header(f"SERVICE {service_name}") }
|
544
511
|
script = __build_script(app, **kwargs)
|
545
512
|
|
546
|
-
#
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
513
|
+
# Determine which workflow command to use based on app configuration
|
514
|
+
app_config = AppConfig(app.scaffold_namespace_path)
|
515
|
+
if app_config.run_on_local:
|
516
|
+
# Use LocalWorkflowRunCommand for local execution
|
517
|
+
workflow_command = LocalWorkflowRunCommand(
|
518
|
+
app,
|
519
|
+
services=services,
|
520
|
+
script=script,
|
521
|
+
**kwargs
|
522
|
+
)
|
523
|
+
else:
|
524
|
+
if kwargs['mkcert']:
|
525
|
+
_app = ContainerizedApp(app_dir_path, SERVICES_NAMESPACE) if containerized else app
|
526
|
+
__services_mkcert(_app, services)
|
527
|
+
|
528
|
+
# Use DockerWorkflowRunCommand for Docker execution
|
529
|
+
workflow_command = DockerWorkflowRunCommand(
|
530
|
+
app,
|
531
|
+
services=services,
|
532
|
+
script=script,
|
533
|
+
**kwargs
|
534
|
+
)
|
567
535
|
|
568
|
-
|
569
|
-
|
536
|
+
if first_time and not containerized and not dry_run:
|
537
|
+
options = {}
|
570
538
|
|
571
|
-
|
572
|
-
|
573
|
-
if index == len(commands) - 1:
|
574
|
-
attached = not kwargs['detached']
|
575
|
-
extra_compose_path = kwargs['extra_entrypoint_compose_path']
|
539
|
+
if os.getcwd() != app_dir_path:
|
540
|
+
options['app_dir_path'] = app_dir_path
|
576
541
|
|
577
|
-
|
578
|
-
|
579
|
-
extra_compose_path=extra_compose_path,
|
580
|
-
namespace=kwargs['namespace'],
|
581
|
-
no_build=kwargs['no_build'],
|
582
|
-
pull=kwargs['pull'],
|
583
|
-
user_id=kwargs['user_id']
|
584
|
-
)
|
585
|
-
if not exec_command:
|
586
|
-
continue
|
542
|
+
if kwargs['namespace'] != kwargs['workflow_name']:
|
543
|
+
options['namespace'] = kwargs['namespace']
|
587
544
|
|
588
|
-
|
589
|
-
|
590
|
-
|
545
|
+
options_str = ' '.join([f"--{key} {value}" for key, value in options.items()])
|
546
|
+
if options_str:
|
547
|
+
options_str = f" {options_str}"
|
591
548
|
|
549
|
+
Logger.instance(LOG_ID).info(f"To view logs, run `stoobly-agent workflow logs{options_str} {kwargs['workflow_name']}`")
|
550
|
+
|
551
|
+
# Execute the workflow
|
552
|
+
workflow_command.up(
|
553
|
+
**command_args,
|
554
|
+
**kwargs
|
555
|
+
)
|
592
556
|
|
593
557
|
@workflow.command(
|
594
558
|
help="Validate a scaffold workflow"
|
@@ -755,16 +719,6 @@ def __get_services(app: App, **kwargs):
|
|
755
719
|
def __print_header(text: str):
|
756
720
|
Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}{text}{bcolors.ENDC}")
|
757
721
|
|
758
|
-
def __run_script(script: TextIOWrapper, dry_run = False):
|
759
|
-
script.close()
|
760
|
-
|
761
|
-
with open(script.name, 'r') as fp:
|
762
|
-
for line in fp:
|
763
|
-
if not dry_run:
|
764
|
-
exec_stream(line.strip())
|
765
|
-
else:
|
766
|
-
print(line.strip())
|
767
|
-
|
768
722
|
def __scaffold_build(app, **kwargs):
|
769
723
|
command = ServiceCreateCommand(app, **kwargs)
|
770
724
|
|
@@ -158,10 +158,10 @@ if is_local:
|
|
158
158
|
Configure which tests to print. Defaults to {test_output_level.PASSED}.
|
159
159
|
'''
|
160
160
|
)
|
161
|
-
@click.option('--public-directory-path', help='Path to public files. Used for mocking requests.')
|
161
|
+
@click.option('--public-directory-path', multiple=True, help='Path to public files. Used for mocking requests. Can take the form <FOLDER-PATH>[:<ORIGIN>].')
|
162
162
|
@ConditionalDecorator(lambda f: click.option('--remote-project-key', help='Use remote project for endpoint definitions.')(f), is_remote)
|
163
163
|
@ConditionalDecorator(lambda f: click.option('--report-key', help='Save results to report.')(f), is_remote)
|
164
|
-
@click.option('--response-fixtures-path', help='Path to response fixtures yaml. Used for mocking requests.')
|
164
|
+
@click.option('--response-fixtures-path', multiple=True, help='Path to response fixtures yaml. Used for mocking requests. Can take the form <FILE-PATH>[:<ORIGIN>].')
|
165
165
|
@ConditionalDecorator(lambda f: click.option('--save', is_flag=True, default=False, help='Save results.')(f), is_remote)
|
166
166
|
@click.option('--scheme', help='Rewrite request scheme.')
|
167
167
|
@click.option(
|
@@ -15,10 +15,10 @@ class TestOptions(TypedDict):
|
|
15
15
|
lifecycle_hooks_path: str
|
16
16
|
log_level: logger.LogLevel
|
17
17
|
output_level: test_output_level.TestOutputLevel
|
18
|
-
public_directory_path: str
|
18
|
+
public_directory_path: str # Comma-separated list of paths, optionally with origin suffix (e.g., "/path/to/files:example.com,/other/path")
|
19
19
|
remote_project_key: str
|
20
20
|
report_key: str
|
21
|
-
response_fixtures_path: str
|
21
|
+
response_fixtures_path: str # Comma-separated list of paths, optionally with origin suffix (e.g., "/path/to/fixtures.yml:example.com,/other/fixtures.yml")
|
22
22
|
save: str
|
23
23
|
scheme: str
|
24
24
|
strategy: test_strategy.TestStrategy
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TypedDict
|
1
|
+
from typing import TypedDict, List, Callable, Optional
|
2
2
|
|
3
3
|
class ComposeOptions(TypedDict):
|
4
4
|
namespace: str
|
@@ -14,5 +14,54 @@ class DownOptions(ComposeOptions):
|
|
14
14
|
|
15
15
|
class UpOptions(ComposeOptions):
|
16
16
|
attached: bool
|
17
|
-
|
18
|
-
|
17
|
+
pull: bool
|
18
|
+
|
19
|
+
# Workflow-specific option types
|
20
|
+
class WorkflowDownOptions(TypedDict, total=False):
|
21
|
+
print_service_header: Optional[Callable[[str], None]]
|
22
|
+
extra_entrypoint_compose_path: Optional[str]
|
23
|
+
namespace: Optional[str]
|
24
|
+
rmi: bool
|
25
|
+
user_id: Optional[int]
|
26
|
+
# CLI-specific options that get passed through
|
27
|
+
containerized: bool
|
28
|
+
dry_run: bool
|
29
|
+
log_level: str
|
30
|
+
script_path: Optional[str]
|
31
|
+
service: List[str]
|
32
|
+
app_dir_path: Optional[str]
|
33
|
+
context_dir_path: Optional[str]
|
34
|
+
|
35
|
+
class WorkflowUpOptions(TypedDict, total=False):
|
36
|
+
print_service_header: Optional[Callable[[str], None]]
|
37
|
+
extra_entrypoint_compose_path: Optional[str]
|
38
|
+
namespace: Optional[str]
|
39
|
+
pull: bool
|
40
|
+
user_id: Optional[int]
|
41
|
+
detached: bool
|
42
|
+
# CLI-specific options that get passed through
|
43
|
+
containerized: bool
|
44
|
+
dry_run: bool
|
45
|
+
log_level: str
|
46
|
+
script_path: Optional[str]
|
47
|
+
service: List[str]
|
48
|
+
no_publish: bool
|
49
|
+
verbose: bool
|
50
|
+
mkcert: bool
|
51
|
+
app_dir_path: Optional[str]
|
52
|
+
ca_certs_dir_path: Optional[str]
|
53
|
+
certs_dir_path: Optional[str]
|
54
|
+
context_dir_path: Optional[str]
|
55
|
+
|
56
|
+
class WorkflowLogsOptions(TypedDict, total=False):
|
57
|
+
print_service_header: Optional[Callable[[str], None]]
|
58
|
+
container: List[str]
|
59
|
+
follow: bool
|
60
|
+
namespace: Optional[str]
|
61
|
+
# CLI-specific options that get passed through
|
62
|
+
containerized: bool
|
63
|
+
dry_run: bool
|
64
|
+
log_level: str
|
65
|
+
script_path: Optional[str]
|
66
|
+
service: List[str]
|
67
|
+
app_dir_path: Optional[str]
|
@@ -127,7 +127,7 @@ def eval_request_with_retry(context: MockContext, eval_request, **options: MockO
|
|
127
127
|
fixture = eval_fixtures(
|
128
128
|
request,
|
129
129
|
public_directory_path=intercept_settings.public_directory_path,
|
130
|
-
|
130
|
+
response_fixtures_path=intercept_settings.response_fixtures_path
|
131
131
|
)
|
132
132
|
if fixture:
|
133
133
|
res = fixture
|
@@ -38,9 +38,6 @@ class InterceptSettings:
|
|
38
38
|
self.__lifecycle_hooks = None
|
39
39
|
self.__initialize_lifecycle_hooks()
|
40
40
|
|
41
|
-
self.__response_fixtures = None
|
42
|
-
self.__initialize_response_fixtures()
|
43
|
-
|
44
41
|
self._mock_rewrite_rules = None
|
45
42
|
self._record_rewrite_rules = None
|
46
43
|
self._replay_rewrite_rules = None
|
@@ -110,11 +107,12 @@ class InterceptSettings:
|
|
110
107
|
|
111
108
|
@property
|
112
109
|
def public_directory_path(self):
|
110
|
+
"""Get raw public directory paths string from environment or headers."""
|
113
111
|
if self.__headers and custom_headers.PUBLIC_DIRECTORY_PATH in self.__headers:
|
114
112
|
return self.__headers[custom_headers.PUBLIC_DIRECTORY_PATH]
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
elif os.environ.get(env_vars.AGENT_PUBLIC_DIRECTORY_PATH):
|
114
|
+
return os.environ[env_vars.AGENT_PUBLIC_DIRECTORY_PATH]
|
115
|
+
return None
|
118
116
|
|
119
117
|
@property
|
120
118
|
def remote_project_key(self):
|
@@ -130,16 +128,13 @@ class InterceptSettings:
|
|
130
128
|
|
131
129
|
@property
|
132
130
|
def response_fixtures_path(self):
|
131
|
+
"""Returns comma-separated list of response fixtures paths, optionally with origin prefix."""
|
133
132
|
if self.__headers and custom_headers.RESPONSE_FIXTURES_PATH in self.__headers:
|
134
133
|
return self.__headers[custom_headers.RESPONSE_FIXTURES_PATH]
|
135
134
|
|
136
135
|
if os.environ.get(env_vars.AGENT_RESPONSE_FIXTURES_PATH):
|
137
136
|
return os.environ[env_vars.AGENT_RESPONSE_FIXTURES_PATH]
|
138
137
|
|
139
|
-
@property
|
140
|
-
def response_fixtures(self):
|
141
|
-
return self.__response_fixtures or {}
|
142
|
-
|
143
138
|
@property
|
144
139
|
def parsed_remote_project_key(self):
|
145
140
|
try:
|
@@ -333,21 +328,6 @@ class InterceptSettings:
|
|
333
328
|
except Exception as e:
|
334
329
|
return Logger.instance().error(e)
|
335
330
|
|
336
|
-
def __initialize_response_fixtures(self):
|
337
|
-
fixtures_path = self.response_fixtures_path
|
338
|
-
|
339
|
-
if not fixtures_path:
|
340
|
-
return
|
341
|
-
|
342
|
-
if not os.path.exists(fixtures_path):
|
343
|
-
return Logger.instance().error(f"Response fixtures {fixtures_path} does not exist")
|
344
|
-
|
345
|
-
with open(fixtures_path, 'r') as stream:
|
346
|
-
try:
|
347
|
-
self.__response_fixtures = yaml.safe_load(stream)
|
348
|
-
except yaml.YAMLError as exc:
|
349
|
-
Logger.instance().error(exc)
|
350
|
-
|
351
331
|
def __order(self, mode):
|
352
332
|
if mode == intercept_mode.RECORD:
|
353
333
|
return self.__data_rules.record_order
|