stoobly-agent 1.9.10__py3-none-any.whl → 1.9.12__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.
Files changed (27) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/cli/helpers/shell.py +1 -3
  3. stoobly_agent/app/cli/intercept_cli.py +1 -1
  4. stoobly_agent/app/cli/scaffold/app_command.py +1 -1
  5. stoobly_agent/app/cli/scaffold/app_config.py +13 -1
  6. stoobly_agent/app/cli/scaffold/app_create_command.py +8 -1
  7. stoobly_agent/app/cli/scaffold/command.py +3 -3
  8. stoobly_agent/app/cli/scaffold/constants.py +3 -1
  9. stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py +6 -6
  10. stoobly_agent/app/cli/scaffold/env.py +0 -2
  11. stoobly_agent/app/cli/scaffold/service_command.py +1 -1
  12. stoobly_agent/app/cli/scaffold/service_config.py +10 -6
  13. stoobly_agent/app/cli/scaffold/service_create_command.py +1 -1
  14. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +14 -10
  15. stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.template.yml +1 -1
  16. stoobly_agent/app/cli/scaffold/workflow_command.py +3 -3
  17. stoobly_agent/app/cli/scaffold/workflow_env.py +10 -3
  18. stoobly_agent/app/cli/scaffold/workflow_namesapce.py +47 -0
  19. stoobly_agent/app/cli/scaffold/workflow_run_command.py +15 -5
  20. stoobly_agent/app/cli/scaffold_cli.py +17 -10
  21. stoobly_agent/app/proxy/mock/eval_fixtures_service.py +33 -2
  22. stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +14 -2
  23. {stoobly_agent-1.9.10.dist-info → stoobly_agent-1.9.12.dist-info}/METADATA +1 -1
  24. {stoobly_agent-1.9.10.dist-info → stoobly_agent-1.9.12.dist-info}/RECORD +27 -26
  25. {stoobly_agent-1.9.10.dist-info → stoobly_agent-1.9.12.dist-info}/LICENSE +0 -0
  26. {stoobly_agent-1.9.10.dist-info → stoobly_agent-1.9.12.dist-info}/WHEEL +0 -0
  27. {stoobly_agent-1.9.10.dist-info → stoobly_agent-1.9.12.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  COMMAND = 'stoobly-agent'
2
- VERSION = '1.9.10'
2
+ VERSION = '1.9.12'
@@ -1,11 +1,9 @@
1
1
  import subprocess
2
2
  import sys
3
3
 
4
- from dotenv import load_dotenv
4
+ DOTENV_PATH = 'STOOBLY_DOTENV_PATH'
5
5
 
6
6
  def exec_stream(command):
7
- load_dotenv()
8
-
9
7
  # Start the process
10
8
  process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
11
9
 
@@ -8,7 +8,7 @@ from stoobly_agent.lib.api.keys.project_key import ProjectKey
8
8
 
9
9
  from .helpers.handle_config_update_service import handle_intercept_active_update, handle_order_update
10
10
 
11
- settings = Settings.instance()
11
+ settings: Settings = Settings.instance()
12
12
 
13
13
  mode_options = [mode.MOCK, mode.RECORD, mode.REPLAY]
14
14
 
@@ -40,7 +40,7 @@ class AppCommand(Command):
40
40
  @property
41
41
  def networks_compose_relative_path(self):
42
42
  return os.path.join(
43
- self.namespace,
43
+ self.scaffold_namespace,
44
44
  DOCKER_COMPOSE_NETWORKS
45
45
  )
46
46
 
@@ -1,16 +1,25 @@
1
1
  from .config import Config
2
- from .constants import APP_NAME_ENV, APP_UI_PORT_ENV
2
+ from .constants import APP_DOCKER_SOCKET_PATH_ENV, APP_NAME_ENV, APP_UI_PORT_ENV
3
3
 
4
4
  class AppConfig(Config):
5
5
 
6
6
  def __init__(self, dir: str):
7
7
  super().__init__(dir)
8
8
 
9
+ self.__docker_socket_path = '/var/run/docker.sock'
9
10
  self.__name = None
10
11
  self.__ui_port = None
11
12
 
12
13
  self.load()
13
14
 
15
+ @property
16
+ def docker_socket_path(self):
17
+ return self.__docker_socket_path
18
+
19
+ @docker_socket_path.setter
20
+ def docker_socket_path(self, v):
21
+ self.__docker_socket_path = v
22
+
14
23
  @property
15
24
  def name(self):
16
25
  return self.__name
@@ -36,6 +45,9 @@ class AppConfig(Config):
36
45
  def write(self):
37
46
  config = {}
38
47
 
48
+ if self.docker_socket_path:
49
+ config[APP_DOCKER_SOCKET_PATH_ENV] = self.docker_socket_path
50
+
39
51
  if self.name:
40
52
  config[APP_NAME_ENV] = self.name
41
53
 
@@ -7,8 +7,8 @@ from .app import App
7
7
  from .app_command import AppCommand
8
8
 
9
9
  class AppCreateOptions(TypedDict):
10
+ docker_socket_path: str
10
11
  name: str
11
- network: str
12
12
  ui_port: int
13
13
 
14
14
  class AppCreateCommand(AppCommand):
@@ -19,9 +19,16 @@ class AppCreateCommand(AppCommand):
19
19
  if kwargs.get('app_name'):
20
20
  self.app_config.name = kwargs['app_name']
21
21
 
22
+ if kwargs.get('docker_socket_path'):
23
+ self.app_config.docker_socket_path = kwargs['docker_socket_path']
24
+
22
25
  if kwargs.get('ui_port'):
23
26
  self.app_config.ui_port = kwargs['ui_port']
24
27
 
28
+ @property
29
+ def docker_socket_path(self):
30
+ return self.app_config.docker_socket_path
31
+
25
32
  @property
26
33
  def app_name(self):
27
34
  return self.app_config.name
@@ -4,12 +4,12 @@ class Command():
4
4
 
5
5
  def __init__(self, app: App):
6
6
  self.__app = app
7
- self.__namespace = app.scaffold_namespace
7
+ self.__scaffold_namespace = app.scaffold_namespace
8
8
 
9
9
  @property
10
10
  def app(self):
11
11
  return self.__app
12
12
 
13
13
  @property
14
- def namespace(self):
15
- return self.__namespace
14
+ def scaffold_namespace(self):
15
+ return self.__scaffold_namespace
@@ -6,6 +6,7 @@ from stoobly_agent.config.data_dir import CERTS_DIR_NAME, DATA_DIR_NAME
6
6
 
7
7
  APP_DIR = '${APP_DIR}'
8
8
  APP_DIR_ENV = 'APP_DIR'
9
+ APP_DOCKER_SOCKET_PATH_ENV = 'APP_DOCKER_SOCKET_PATH'
9
10
  APP_NETWORK_ENV = 'APP_NETWORK'
10
11
  APP_NAME_ENV = 'APP_NAME'
11
12
  APP_UI_PORT_ENV = 'APP_UI_PORT'
@@ -16,7 +17,8 @@ COMPOSE_TEMPLATE = '.docker-compose.{workflow}.yml'
16
17
  CONFIG_FILE = '.config.yml'
17
18
  CONTEXT_DIR_ENV = 'CONTEXT_DIR'
18
19
  DOCKER_NAMESPACE = 'docker'
19
- ENV_FILE = '.env'
20
+ DOTENV_FILE = '.env'
21
+ DOTENV_PATH_ENV = 'STOOBLY_DOTENV_PATH'
20
22
  NAMESERVERS_FILE = '.nameservers'
21
23
  PUBLIC_FOLDER_NAME = 'public'
22
24
  SERVICE_DETACHED = '${SERVICE_DETACHED}'
@@ -4,13 +4,13 @@ import yaml
4
4
 
5
5
  from typing import List
6
6
 
7
- from stoobly_agent.config.data_dir import DATA_DIR_NAME, TMP_DIR_NAME
8
7
  from stoobly_agent.app.cli.scaffold.constants import APP_DIR
9
8
  from stoobly_agent.app.cli.scaffold.service_config import ServiceConfig
10
9
  from stoobly_agent.app.cli.scaffold.docker.constants import APP_INGRESS_NETWORK_NAME, APP_EGRESS_NETWORK_NAME, DOCKER_COMPOSE_BASE, DOCKER_COMPOSE_BASE_TEMPLATE
11
10
  from stoobly_agent.app.cli.scaffold.templates.constants import CORE_GATEWAY_SERVICE_NAME
11
+ from stoobly_agent.app.cli.scaffold.workflow_namesapce import WorkflowNamespace
12
12
 
13
- def configure_gateway(namespace: str, service_paths: List[str], no_publish = False):
13
+ def configure_gateway(workflow_namespace: WorkflowNamespace, service_paths: List[str], no_publish = False):
14
14
  if len(service_paths) == 0:
15
15
  return
16
16
 
@@ -36,7 +36,7 @@ def configure_gateway(namespace: str, service_paths: List[str], no_publish = Fal
36
36
  gateway_base['ports'] = ports
37
37
 
38
38
  app_dir_path = os.path.dirname(os.path.dirname(service_dir_path))
39
- __with_traefik_config(namespace, service_paths, app_dir_path, gateway_base)
39
+ __with_traefik_config(workflow_namespace, service_paths, app_dir_path, gateway_base)
40
40
  __with_networks(gateway_base, hostnames)
41
41
 
42
42
  with open(docker_compose_dest_path, 'w') as fp:
@@ -50,7 +50,7 @@ def __with_networks(config: dict, hostnames: List[str]):
50
50
  'aliases': hostnames
51
51
  }
52
52
 
53
- def __with_traefik_config(namespace: str, service_paths: List[str], app_dir_path: str, compose: dict):
53
+ def __with_traefik_config(workflow_namespace: WorkflowNamespace, service_paths: List[str], app_dir_path: str, compose: dict):
54
54
  config_dest = '/etc/traefik/traefik.yml'
55
55
 
56
56
  if not compose['volumes']:
@@ -98,8 +98,8 @@ def __with_traefik_config(namespace: str, service_paths: List[str], app_dir_path
98
98
  })
99
99
 
100
100
  # Create traefik.yml in .stoobly/tmp
101
- traefik_template_relative_path = os.path.join(DATA_DIR_NAME, TMP_DIR_NAME, namespace, 'traefik.yml')
102
- traefik_template_dest_path = os.path.join(app_dir_path, traefik_template_relative_path)
101
+ traefik_template_relative_path = workflow_namespace.traefik_config_relative_path(app_dir_path)
102
+ traefik_template_dest_path = workflow_namespace.traefik_config_path
103
103
 
104
104
  if not os.path.exists(os.path.dirname(traefik_template_dest_path)):
105
105
  os.makedirs(os.path.dirname(traefik_template_dest_path), exist_ok=True)
@@ -1,7 +1,5 @@
1
1
  import os
2
2
 
3
- from .constants import ENV_FILE
4
-
5
3
  DELIMITTER = "\n"
6
4
 
7
5
  class Env():
@@ -48,7 +48,7 @@ class ServiceCommand(AppCommand):
48
48
  @property
49
49
  def service_relative_path(self):
50
50
  return os.path.join(
51
- self.namespace,
51
+ self.scaffold_namespace,
52
52
  self.service_name,
53
53
  )
54
54
 
@@ -56,7 +56,7 @@ class ServiceConfig(Config):
56
56
 
57
57
  @property
58
58
  def id(self):
59
- return hashlib.md5(self.dir.encode()).hexdigest()
59
+ return hashlib.md5(self.url.encode()).hexdigest()
60
60
 
61
61
  @property
62
62
  def hostname(self):
@@ -107,18 +107,22 @@ class ServiceConfig(Config):
107
107
  if not self.hostname:
108
108
  return ''
109
109
 
110
- _proxy_mode = f"reverse:{self.scheme}://{self.hostname}"
110
+ return f"reverse:{self.url}"
111
+
112
+ @property
113
+ def url(self):
114
+ _url = f"{self.scheme}://{self.hostname}"
111
115
 
112
116
  if not self.port:
113
- return _proxy_mode
117
+ return _url
114
118
 
115
119
  if self.scheme == 'http' and self.port == 80:
116
- return _proxy_mode
120
+ return _url
117
121
 
118
122
  if self.scheme == 'https' and self.port == 443:
119
- return _proxy_mode
123
+ return _url
120
124
 
121
- return f"{_proxy_mode}:{self.port}"
125
+ return f"{_url}:{self.port}"
122
126
 
123
127
  @proxy_mode.setter
124
128
  def proxy_mode(self, v):
@@ -37,7 +37,7 @@ class ServiceCreateCommand(ServiceCommand):
37
37
 
38
38
  workflow_kwargs = {
39
39
  'app_dir_path': self.app_dir_path,
40
- 'namespace': self.namespace,
40
+ 'namespace': self.scaffold_namespace,
41
41
  'service_name': self.service_name,
42
42
  }
43
43
 
@@ -4,7 +4,7 @@
4
4
  # STOOBLY_CA_CERTS_DIR: path to folder where ca certs are stored, defaults to $(pwd)/.stoobly/ca_certs
5
5
  # STOOBLY_CERTS_DIR: path to a folder to store certs, defaults to $(pwd)/.stoobly/certs
6
6
  # STOOBLY_CONTEXT_DIR: path to the folder containing the .stoobly folder, defaults to $(pwd)
7
- # STOOBLY_ENV_FILE: path to dotenv file, defaults to $(pwd)/.env
7
+ # STOOBLY_DOTENV_FILE: path to dotenv file, defaults to $(pwd)/.env
8
8
  # STOOBLY_WORKFLOW_SERVICE_OPTIONS: extra --service options to pass 'stoobly-agent scaffold workflow' commands
9
9
 
10
10
  # Overridable Options
@@ -28,7 +28,7 @@ app_dir=$$(realpath "$${STOOBLY_APP_DIR:-$(CONTEXT_DIR_DEFAULT)}")
28
28
  ca_certs_dir=$$(realpath "$${STOOBLY_CA_CERTS_DIR:-$(app_data_dir)/ca_certs}")
29
29
  certs_dir=$$(realpath "$${STOOBLY_CERTS_DIR:-$(app_data_dir)/certs}")
30
30
  context_dir=$$(realpath "$${STOOBLY_CONTEXT_DIR:-$(CONTEXT_DIR_DEFAULT)}")
31
- env_file=$$(realpath "$${STOOBLY_ENV_FILE:-.env}" 2> /dev/null || echo '')
31
+ dotenv_file=$$(realpath "$${STOOBLY_DOTENV_FILE:-.env}" 2> /dev/null || echo '')
32
32
  workflow=record
33
33
  workflow_service_options=$(shell echo $$STOOBLY_WORKFLOW_SERVICE_OPTIONS)
34
34
 
@@ -38,6 +38,7 @@ app_tmp_dir=$(app_data_dir)/tmp
38
38
  dockerfile_path=$(app_namespace_dir)/.Dockerfile.context
39
39
  exec_docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.exec.yml
40
40
  workflow_namespace=$(if $(namespace),$(namespace),$(workflow))
41
+ workflow_namespace_dir=$(app_tmp_dir)/$(workflow_namespace)
41
42
  workflow_script=.stoobly/tmp/$(workflow_namespace)/run.sh
42
43
 
43
44
  # Options
@@ -48,24 +49,23 @@ workflow_down_options=$(working_dir_options) --user-id $(USER_ID) $(workflow_dow
48
49
  workflow_log_options=$(workflow_log_extra_options)
49
50
  workflow_run_options=--namespace $(workflow_namespace) --script-path $(workflow_script) $(workflow_service_options)
50
51
  workflow_up_options=$(working_dir_options) $(certs_dir_options) --user-id $(USER_ID) $(workflow_up_extra_options)
51
-
52
+
52
53
  # Commands
53
54
  exec_env=APP_DIR="$(app_dir)" CA_CERTS_DIR="$(ca_certs_dir)" USER_ID="$(USER_ID)"
54
55
  exec_up=$(DOCKER_BIN) compose -f "$(exec_docker_compose_file_path)" run --rm stoobly_ui.command
55
- source_env=set -a; [ -f "$(env_file)" ] && . "$(env_file)"; set +a
56
56
 
57
57
  # Build base image
58
58
  stoobly_exec_build=$(DOCKER_BIN) build $(stoobly_exec_build_args) $(app_namespace_dir) > /dev/null
59
59
  stoobly_exec_build_args=-f "$(dockerfile_path)" -t stoobly.$(USER_ID) --build-arg USER_ID=$(USER_ID) $(PULL_OPTION) --quiet
60
60
 
61
61
  # Exec any
62
- stoobly_exec=$(source_env); $(stoobly_exec_build) && $(stoobly_exec_env) $(exec_up)
62
+ stoobly_exec=$(stoobly_exec_build) && $(stoobly_exec_env) $(exec_up)
63
63
  stoobly_exec_env=$(exec_env) CONTEXT_DIR="$(context_dir)"
64
64
 
65
65
  # Exec workflow run
66
66
  # Scaffold is stored in the application source code directory.
67
67
  # When running a scaffold command from within a container, it needs access to $(app_dir) rather than $(context_dir)
68
- stoobly_exec_run=$(source_env); $(stoobly_exec_build) && $(stoobly_exec_run_env) $(exec_up)
68
+ stoobly_exec_run=$(stoobly_exec_build) && $(stoobly_exec_run_env) $(exec_up)
69
69
  stoobly_exec_run_env=$(exec_env) CONTEXT_DIR="$(app_dir)"
70
70
 
71
71
  # Workflow run
@@ -88,14 +88,16 @@ ca-cert/install: stoobly/install
88
88
  certs:
89
89
  @export EXEC_COMMAND=scaffold/.mkcert EXEC_OPTIONS="" EXEC_ARGS="" && \
90
90
  $(stoobly_exec)
91
- nameservers: tmpdir
91
+ dotenv: workflow/namespace
92
+ @if [ -f "$(dotenv_file)" ]; then cp "$(dotenv_file)" $(workflow_namespace_dir)/.env; fi
93
+ nameservers: workflow/namespace
92
94
  @if [ -f /etc/resolv.conf ]; then \
93
95
  nameserver=$$(grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' /etc/resolv.conf) && \
94
96
  if [ "$$nameserver" = "127.0.0.53" ]; then \
95
97
  echo "Nameserver is 127.0.0.53. Checking resolvectl status..."; \
96
98
  nameserver=$$(resolvectl status | sed -n '/DNS Servers:/s/.*DNS Servers:\s*\([^ ]*\).*/\1/p' | head -n 1); \
97
99
  fi; \
98
- echo "$$nameserver" > $(app_tmp_dir)/.nameservers; \
100
+ echo "$$nameserver" > $(workflow_namespace_dir)/.nameservers; \
99
101
  else \
100
102
  echo "/etc/resolv.conf not found." >&2; \
101
103
  fi
@@ -158,7 +160,7 @@ test/logs: workflow/test workflow/logs
158
160
  test/down: workflow/test workflow/down
159
161
  tmpdir:
160
162
  @mkdir -p $(app_tmp_dir)
161
- workflow/down:
163
+ workflow/down: dotenv
162
164
  @export EXEC_COMMAND=scaffold/.down EXEC_OPTIONS="$(workflow_down_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
163
165
  $(stoobly_exec_run) && \
164
166
  $(workflow_run)
@@ -182,6 +184,8 @@ workflow/logs:
182
184
  $(workflow_run)
183
185
  workflow/mock:
184
186
  $(eval workflow=mock)
187
+ workflow/namespace: tmpdir
188
+ @mkdir -p $(workflow_namespace_dir)
185
189
  workflow/record:
186
190
  $(eval workflow=record)
187
191
  workflow/services:
@@ -189,7 +193,7 @@ workflow/services:
189
193
  $(stoobly_exec_run)
190
194
  workflow/test:
191
195
  $(eval workflow=test) $(eval workflow_up_extra_options=$(workflow_up_extra_options) --no-publish)
192
- workflow/up:
196
+ workflow/up: dotenv
193
197
  @export EXEC_COMMAND=scaffold/.up EXEC_OPTIONS="$(workflow_up_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
194
198
  $(stoobly_exec_run) && \
195
199
  $(workflow_run)
@@ -6,5 +6,5 @@ services:
6
6
  profiles:
7
7
  - gateway_base
8
8
  volumes:
9
- - /var/run/docker.sock:/var/run/docker.sock:ro
9
+ - ${APP_DOCKER_SOCKET_PATH}:/var/run/docker.sock:ro
10
10
  - ${CERTS_DIR}:/certs:ro
@@ -5,7 +5,7 @@ import yaml
5
5
  from stoobly_agent.lib.logger import Logger
6
6
 
7
7
  from .app import App
8
- from .constants import BIN_FOLDER_NAME, COMPOSE_TEMPLATE, CONFIG_FILE, ENV_FILE, PUBLIC_FOLDER_NAME
8
+ from .constants import BIN_FOLDER_NAME, COMPOSE_TEMPLATE, CONFIG_FILE, DOTENV_FILE, PUBLIC_FOLDER_NAME
9
9
  from .docker.constants import DOCKER_COMPOSE_CUSTOM
10
10
  from .service_command import ServiceCommand
11
11
  from .workflow_config import WorkflowConfig
@@ -101,8 +101,8 @@ class WorkflowCommand(ServiceCommand):
101
101
  return os.path.join(self.workflow_path, CONFIG_FILE)
102
102
 
103
103
  @property
104
- def workflow_env_path(self):
105
- return os.path.join(self.workflow_path, ENV_FILE)
104
+ def workflow_dotenv_path(self):
105
+ return os.path.join(self.workflow_path, DOTENV_FILE)
106
106
 
107
107
  @property
108
108
  def workflow_exists(self):
@@ -1,11 +1,15 @@
1
+ import os
2
+
3
+ from dotenv import dotenv_values
4
+
1
5
  from .config import Config
2
- from .constants import ENV_FILE
6
+ from .constants import DOTENV_FILE
3
7
  from .env import Env
4
8
 
5
9
  class WorkflowEnv(Config):
6
10
 
7
11
  def __init__(self, dir: str):
8
- super().__init__(dir, ENV_FILE)
12
+ super().__init__(dir, DOTENV_FILE)
9
13
 
10
14
  self.__env_vars = {}
11
15
 
@@ -15,6 +19,9 @@ class WorkflowEnv(Config):
15
19
  def load(self):
16
20
  self.__env_vars = self.read()
17
21
 
18
- def write(self, env_vars: dict):
22
+ def write(self, env_vars: dict, dotenv_path: str = None):
23
+ if dotenv_path and os.path.exists(dotenv_path):
24
+ env_vars = { **env_vars, **dotenv_values(dotenv_path) }
25
+
19
26
  Env(self.path).write(env_vars)
20
27
  self.__env_vars = env_vars
@@ -0,0 +1,47 @@
1
+ import os
2
+ import shutil
3
+
4
+ from .app import App
5
+ from .constants import DOTENV_PATH_ENV, DOTENV_FILE, NAMESERVERS_FILE
6
+
7
+ class WorkflowNamespace():
8
+
9
+ def __init__(self, app: App, namespace: str = None):
10
+ self._app = app
11
+ self._namespace = namespace
12
+ self._path = os.path.join(app.data_dir.tmp_dir_path, namespace or '')
13
+
14
+ @property
15
+ def dotenv_path(self):
16
+ return os.path.join(self.path, DOTENV_FILE)
17
+
18
+ @property
19
+ def nameservers_path(self):
20
+ return os.path.join(self.path, NAMESERVERS_FILE)
21
+
22
+ @property
23
+ def namespace(self):
24
+ return self._namespace
25
+
26
+ @property
27
+ def path(self):
28
+ return self._path
29
+
30
+ @property
31
+ def run_script_path(self):
32
+ return os.path.join(self.path, 'run.sh')
33
+
34
+ @property
35
+ def traefik_config_path(self):
36
+ return os.path.join(self.path, 'traefik.yml')
37
+
38
+ def copy_dotenv(self):
39
+ dotenv_path = os.environ.get(DOTENV_PATH_ENV) or '.env'
40
+
41
+ if os.path.isfile(dotenv_path):
42
+ shutil.copy(dotenv_path, self.dotenv_path)
43
+
44
+ def traefik_config_relative_path(self, path: str):
45
+ if not path:
46
+ return path
47
+ return self.traefik_config_path.replace(path if path[len(path) - 1] == '/' else path + '/', '', 1)
@@ -1,22 +1,23 @@
1
1
  import dns.resolver
2
- import hashlib
3
2
  import os
4
3
  import pdb
5
4
  import subprocess
6
5
  import re
7
6
 
7
+
8
8
  from stoobly_agent.config.data_dir import DataDir
9
9
  from stoobly_agent.lib.logger import Logger
10
10
 
11
11
  from .app import App
12
12
  from .constants import (
13
- APP_DIR_ENV, APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV, NAMESERVERS_FILE,
13
+ APP_DIR_ENV, APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV,
14
14
  SERVICE_DNS_ENV, SERVICE_NAME_ENV, SERVICE_SCRIPTS_DIR, SERVICE_SCRIPTS_ENV, USER_ID_ENV,
15
15
  WORKFLOW_NAME_ENV, WORKFLOW_NAMESPACE_ENV, WORKFLOW_SCRIPTS_DIR, WORKFLOW_SCRIPTS_ENV, WORKFLOW_TEMPLATE_ENV
16
16
  )
17
17
  from .docker.constants import APP_EGRESS_NETWORK_TEMPLATE, APP_INGRESS_NETWORK_TEMPLATE, DOCKERFILE_CONTEXT
18
18
  from .workflow_command import WorkflowCommand
19
19
  from .workflow_env import WorkflowEnv
20
+ from .workflow_namesapce import WorkflowNamespace
20
21
  from ..types.workflow_run_command import BuildOptions, ComposeOptions, DownOptions, UpOptions
21
22
 
22
23
  LOG_ID = 'WorkflowRunCommand'
@@ -30,7 +31,8 @@ class WorkflowRunCommand(WorkflowCommand):
30
31
  self.__ca_certs_dir_path = kwargs.get('ca_certs_dir_path') or app.ca_certs_dir_path
31
32
  self.__certs_dir_path = kwargs.get('certs_dir_path') or app.certs_dir_path
32
33
  self.__context_dir_path = kwargs.get('context_dir_path') or app.context_dir_path
33
- self.__network = f"{kwargs.get('namespace') or self.workflow_name}.{kwargs.get('network') or app.network}"
34
+ self.__namespace = kwargs.get('namespace') or self.workflow_name
35
+ self.__network = f"{self.__namespace}.{kwargs.get('network') or app.network}"
34
36
 
35
37
  @property
36
38
  def app_dir_path(self):
@@ -60,6 +62,10 @@ class WorkflowRunCommand(WorkflowCommand):
60
62
  def current_working_dir(self, v):
61
63
  self.__current_working_dir = v
62
64
 
65
+ @property
66
+ def dotenv_path(self):
67
+ return WorkflowNamespace(self.app, self.namespace).dotenv_path
68
+
63
69
  @property
64
70
  def nameservers(self):
65
71
  path = self.nameservers_path
@@ -72,7 +78,11 @@ class WorkflowRunCommand(WorkflowCommand):
72
78
 
73
79
  @property
74
80
  def nameservers_path(self):
75
- return os.path.join(self.app.data_dir.tmp_dir_path, NAMESERVERS_FILE)
81
+ return WorkflowNamespace(self.app, self.namespace).nameservers_path
82
+
83
+ @property
84
+ def namespace(self):
85
+ return self.__namespace
76
86
 
77
87
  @property
78
88
  def network(self):
@@ -272,7 +282,7 @@ class WorkflowRunCommand(WorkflowCommand):
272
282
  _config[SERVICE_DNS_ENV] = '8.8.8.8'
273
283
 
274
284
  env_vars = self.config(_config)
275
- WorkflowEnv(self.workflow_path).write(env_vars)
285
+ WorkflowEnv(self.workflow_path).write(env_vars, self.dotenv_path)
276
286
  return env_vars
277
287
 
278
288
  def __find_nameservers(self, dns_resolver: dns.resolver.Resolver):
@@ -30,6 +30,7 @@ from stoobly_agent.app.cli.scaffold.workflow import Workflow
30
30
  from stoobly_agent.app.cli.scaffold.workflow_create_command import WorkflowCreateCommand
31
31
  from stoobly_agent.app.cli.scaffold.workflow_copy_command import WorkflowCopyCommand
32
32
  from stoobly_agent.app.cli.scaffold.workflow_log_command import WorkflowLogCommand
33
+ from stoobly_agent.app.cli.scaffold.workflow_namesapce import WorkflowNamespace
33
34
  from stoobly_agent.app.cli.scaffold.workflow_run_command import WorkflowRunCommand
34
35
  from stoobly_agent.app.cli.scaffold.workflow_validate_command import WorkflowValidateCommand
35
36
  from stoobly_agent.config.constants import env_vars
@@ -88,6 +89,7 @@ def hostname(ctx):
88
89
  help="Scaffold application"
89
90
  )
90
91
  @click.option('--app-dir-path', default=current_working_dir, help='Path to create the app scaffold.')
92
+ @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.')
91
93
  @click.option('--quiet', is_flag=True, help='Disable log output.')
92
94
  @click.option('--ui-port', default=4200, type=click.IntRange(1, 65535), help='UI service port.')
93
95
  @click.argument('app_name', callback=validate_app_name)
@@ -337,7 +339,7 @@ def copy(**kwargs):
337
339
  @click.option('--service', multiple=True, help='Select which services to log. Defaults to all.')
338
340
  @click.option('--user-id', default=os.getuid(), help='OS user ID of the owner of context dir path.')
339
341
  @click.argument('workflow_name')
340
- def down(**kwargs):
342
+ def down(**kwargs):
341
343
  os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
342
344
 
343
345
  containerized = kwargs['containerized']
@@ -347,6 +349,7 @@ def down(**kwargs):
347
349
  __validate_app(app)
348
350
 
349
351
  __with_namespace_defaults(kwargs)
352
+ __with_workflow_namespace(app, kwargs['namespace'])
350
353
 
351
354
  services = __get_services(
352
355
  app, service=kwargs['service'], workflow=[kwargs['workflow_name']]
@@ -360,7 +363,7 @@ def down(**kwargs):
360
363
  command.current_working_dir = current_working_dir
361
364
  commands.append(command)
362
365
 
363
- script = __build_script(**kwargs)
366
+ script = __build_script(app, **kwargs)
364
367
 
365
368
  commands = sorted(commands, key=lambda command: command.service_config.priority)
366
369
  for index, command in enumerate(commands):
@@ -449,7 +452,7 @@ def logs(**kwargs):
449
452
  command = WorkflowLogCommand(app, **config)
450
453
  commands.append(command)
451
454
 
452
- script = __build_script(**kwargs)
455
+ script = __build_script(app, **kwargs)
453
456
 
454
457
  commands = sorted(commands, key=lambda command: command.service_config.priority)
455
458
  for index, command in enumerate(commands):
@@ -501,6 +504,7 @@ def up(**kwargs):
501
504
  __validate_app(app)
502
505
 
503
506
  __with_namespace_defaults(kwargs)
507
+ workflow_namespace = __with_workflow_namespace(app, kwargs['namespace'])
504
508
 
505
509
  services = __get_services(
506
510
  app, service=kwargs['service'], workflow=[kwargs['workflow_name']]
@@ -512,9 +516,7 @@ def up(**kwargs):
512
516
 
513
517
  # Gateway ports are dynamically set depending on the workflow run
514
518
  workflow = Workflow(kwargs['workflow_name'], app)
515
- configure_gateway(
516
- kwargs['namespace'] or workflow.workflow_name, workflow.service_paths_from_services(services), kwargs['no_publish']
517
- )
519
+ configure_gateway(workflow_namespace, workflow.service_paths_from_services(services), kwargs['no_publish'])
518
520
 
519
521
  commands: List[WorkflowRunCommand] = []
520
522
  for service in services:
@@ -524,7 +526,7 @@ def up(**kwargs):
524
526
  command.current_working_dir = current_working_dir
525
527
  commands.append(command)
526
528
 
527
- script = __build_script(**kwargs)
529
+ script = __build_script(app, **kwargs)
528
530
 
529
531
  # Before services can be started, their image and network needs to be created
530
532
  if len(commands) > 0:
@@ -677,11 +679,11 @@ scaffold.add_command(service)
677
679
  scaffold.add_command(workflow)
678
680
  scaffold.add_command(hostname)
679
681
 
680
- def __build_script(**kwargs):
682
+ def __build_script(app: App, **kwargs):
681
683
  script_path = kwargs['script_path']
682
684
  if not script_path:
683
- script_file_name = 'run.sh'
684
- script_path = os.path.join(data_dir.tmp_dir_path, kwargs.get('namespace') or kwargs['workflow_name'] or '', script_file_name)
685
+ workflow_namespace = WorkflowNamespace(app, kwargs.get('namespace') or kwargs['workflow_name'])
686
+ script_path = workflow_namespace.run_script_path
685
687
 
686
688
  script_dir = os.path.dirname(script_path)
687
689
  if not os.path.exists(script_dir):
@@ -836,3 +838,8 @@ def __workflow_create(app, **kwargs):
836
838
  template=kwargs['template'],
837
839
  workflow_decorators=workflow_decorators
838
840
  )
841
+
842
+ def __with_workflow_namespace(app: App, namespace: str):
843
+ workflow_namespace = WorkflowNamespace(app, namespace)
844
+ workflow_namespace.copy_dotenv()
845
+ return workflow_namespace
@@ -7,7 +7,7 @@ from io import BytesIO
7
7
  from mitmproxy.http import Request as MitmproxyRequest
8
8
  from requests import Response
9
9
  from requests.structures import CaseInsensitiveDict
10
- from typing import Union
10
+ from typing import Optional, Union
11
11
 
12
12
  from stoobly_agent.lib.logger import bcolors, Logger
13
13
  from stoobly_agent.config.constants.custom_headers import MOCK_FIXTURE_PATH
@@ -62,7 +62,7 @@ def eval_fixtures(request: MitmproxyRequest, **options: Options) -> Union[Respon
62
62
  with open(fixture_path, 'rb') as fp:
63
63
  response = Response()
64
64
 
65
- response.status_code = status_code
65
+ response.status_code = int(status_code)
66
66
  response.raw = BytesIO(fp.read())
67
67
  headers[MOCK_FIXTURE_PATH] = fixture_path
68
68
  response.headers = headers
@@ -71,6 +71,11 @@ def eval_fixtures(request: MitmproxyRequest, **options: Options) -> Union[Respon
71
71
  content_type = __guess_content_type(fixture_path)
72
72
  if content_type:
73
73
  response.headers['content-type'] = content_type
74
+ else:
75
+ # Default to highest priority accept header
76
+ content_type = __choose_highest_priority_content_type(request.headers.get('accept'))
77
+ if content_type:
78
+ response.headers['content-type'] = content_type
74
79
 
75
80
  Logger.instance(LOG_ID).debug(f"{bcolors.OKBLUE}Resolved{bcolors.ENDC} fixture {fixture_path}")
76
81
 
@@ -125,6 +130,32 @@ def __eval_response_fixtures(request: MitmproxyRequest, response_fixtures: Fixtu
125
130
  if path:
126
131
  return fixture
127
132
 
133
+ def __choose_highest_priority_content_type(accept_header: str) -> Optional[str]:
134
+ if not accept_header:
135
+ return None
136
+
137
+ types = []
138
+ for part in accept_header.split(","):
139
+ media_range = part.strip()
140
+ if ";" in media_range:
141
+ mime, *params = media_range.split(";")
142
+ q = 1.0 # default
143
+ for param in params:
144
+ param = param.strip()
145
+ if param.startswith("q="):
146
+ try:
147
+ q = float(param[2:])
148
+ except ValueError:
149
+ q = 0.0 # invalid q values treated as lowest
150
+ else:
151
+ mime = media_range
152
+ q = 1.0
153
+ types.append((mime.strip(), q))
154
+
155
+ # Sort by descending q
156
+ types.sort(key=lambda x: -x[1])
157
+ return types[0][0] if types else None
158
+
128
159
  def __parse_accept_header(accept_header):
129
160
  types = []
130
161
  for item in accept_header.split(","):
@@ -2,6 +2,7 @@ import os
2
2
  import pdb
3
3
  import pytest
4
4
  import requests
5
+ import shutil
5
6
 
6
7
  from click.testing import CliRunner
7
8
  from mitmproxy.http import Request as MitmproxyRequest
@@ -156,6 +157,14 @@ class TestEvalFixturesService():
156
157
  assert res != None
157
158
  return res
158
159
 
160
+ @pytest.fixture()
161
+ def public_directory_default_response(self, index_file_path: str, mitmproxy_request: MitmproxyRequest, public_directory: str):
162
+ path = os.path.join(public_directory, 'index')
163
+ shutil.move(index_file_path, path)
164
+ res: requests.Response = eval_fixtures(mitmproxy_request, public_directory_path=public_directory)
165
+ assert res != None
166
+ return res
167
+
159
168
  def test_it_sets_contents(
160
169
  self, public_directory_response: requests.Response, index_file_contents: str
161
170
  ):
@@ -164,5 +173,8 @@ class TestEvalFixturesService():
164
173
  def test_it_headers(self, public_directory_response: requests.Response):
165
174
  assert public_directory_response.headers['Content-Type'] == 'text/html'
166
175
 
167
- def test_it_sets_status_code(self, public_directory_response: requests.Response):
168
- assert public_directory_response.status_code == 200
176
+ def test_it_sets_status_code(self, public_directory_response: requests.Response):
177
+ assert public_directory_response.status_code == 200
178
+
179
+ def test_default_it_headers(self, public_directory_default_response: requests.Response):
180
+ assert public_directory_default_response.headers['Content-Type'] == 'application/json'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stoobly-agent
3
- Version: 1.9.10
3
+ Version: 1.9.12
4
4
  Summary: Record, mock, and test HTTP(s) requests. CLI agent for Stoobly
5
5
  License: Apache-2.0
6
6
  Author: Matt Le
@@ -1,4 +1,4 @@
1
- stoobly_agent/__init__.py,sha256=nIYIzLEOceTM5Bfw040WST6iw0kTxA4hWg7lKhn6tfo,45
1
+ stoobly_agent/__init__.py,sha256=xENhASdGvBATXYrZA-vmnaCPLgkr7RjAYnjGsnkvbrc,45
2
2
  stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
4
4
  stoobly_agent/app/api/application_http_request_handler.py,sha256=Vvz53yB0bR7J-QqMAkLlhcZrA4P64ZEN7w8cMbgl6o0,5261
@@ -52,7 +52,7 @@ stoobly_agent/app/cli/helpers/request_synchronize_handler.py,sha256=wawAL8hoZLqO
52
52
  stoobly_agent/app/cli/helpers/run_command_service.py,sha256=w9CNDZDj_gxCCXAFFOLONuOVu6_W9f8z1RX3-5RKuTM,731
53
53
  stoobly_agent/app/cli/helpers/scenario_facade.py,sha256=nWcycEZBL87OFCCQx85-5QaK0Q9XXk2-RMeC1uef2_Y,3134
54
54
  stoobly_agent/app/cli/helpers/schema_builder.py,sha256=-R_oU_d8nPNdNA31WDp8lCh7XpCeazLm_1dl_Kq8DLU,1127
55
- stoobly_agent/app/cli/helpers/shell.py,sha256=iBzZEgmB7fBsKxF1rm7i_pjCPOiE5LcMD-W1CsX4Yhg,645
55
+ stoobly_agent/app/cli/helpers/shell.py,sha256=vnQuv-wvLepcgSIZrz7e4CsL5HuKhAVYfHKF0WuPrgg,633
56
56
  stoobly_agent/app/cli/helpers/synchronize_request_service.py,sha256=pwsILyrR6XPBJAIVaato0tRrbCT4z5-FymJgfeWTsVw,6018
57
57
  stoobly_agent/app/cli/helpers/tabulate_print_service.py,sha256=d4xtJgn9sueL8nAwOSIyiyzuY1ptsD1wxEdLQe0X2tc,964
58
58
  stoobly_agent/app/cli/helpers/test_facade.py,sha256=gRnbjYNxnzPmtvFp2J6TZgxaMSzDjpAWdb79kaFBt_I,2227
@@ -61,19 +61,19 @@ stoobly_agent/app/cli/helpers/trace_aliases.py,sha256=8N8pnsnONwVv-fabCTvDlXec-W
61
61
  stoobly_agent/app/cli/helpers/trace_context_facade.py,sha256=3MDjY_bdhvE2dad_B_w9gemCZZoiVjotbE8o9hrmVYo,1027
62
62
  stoobly_agent/app/cli/helpers/validations.py,sha256=nvcrEHvjMOAyHlJK0bQIwyzBUciJ0qNkNESa2zZ5ZFo,6232
63
63
  stoobly_agent/app/cli/helpers/verify_raw_request_service.py,sha256=tmLeRBYhNgAQeJD-rF6Nj22F_TfVktVSb1wjRPFblBQ,658
64
- stoobly_agent/app/cli/intercept_cli.py,sha256=UswnJJiBV7FKWaxIpiOYrwPSfMTPeg8UO_h_3gUxdD4,5106
64
+ stoobly_agent/app/cli/intercept_cli.py,sha256=LNHfAec6Tf_cFNwuNvcvig-k9lKi7kVsMGIDC0fWp2o,5116
65
65
  stoobly_agent/app/cli/main_group.py,sha256=3UzBxin2f2p5KNqo6Oayo_I1VI2pHpqOoRexUnJ74a4,2187
66
66
  stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3Nc4fKOc,3852
67
67
  stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
68
68
  stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g0A1CtJ0,7747
69
69
  stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
70
  stoobly_agent/app/cli/scaffold/app.py,sha256=0HJm8cdyIBAdOBr18499MkZGhmEFu6I7GSZXR7pKxkw,3650
71
- stoobly_agent/app/cli/scaffold/app_command.py,sha256=sliaYulMNmaxbRXpJIDtBDWEBGEv5Tht4rpErzC_xxw,2368
72
- stoobly_agent/app/cli/scaffold/app_config.py,sha256=EyFezd7oeYLHmYPEdYlQCSeGP3AM1NmFz2mT9WLp7xY,818
73
- stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=2y1W17r3mxCf-7U7uUT-44icprGPMERCfVITO2NA76w,981
74
- stoobly_agent/app/cli/scaffold/command.py,sha256=aoTsdkkBzyu7TkVSMdNQQGk0Gj874jNgFcjR14y3TuM,254
71
+ stoobly_agent/app/cli/scaffold/app_command.py,sha256=w15VCsyefIE9HfzpIRMrv2BEep46ZSPgvsjZmFOMPZs,2377
72
+ stoobly_agent/app/cli/scaffold/app_config.py,sha256=z4sxZcXZTEoXyUZajUuffuWQN8x8eovu-TBtgoTMXEE,1182
73
+ stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=HOlXL6Ugid2qHagqDVGl3ksjLKE4WF_3Dji8FoQlpuM,1215
74
+ stoobly_agent/app/cli/scaffold/command.py,sha256=G4Zp647cuviaEXUdcl7Rbx_qQAr0Z_DS7-Y3MWDC1Qc,281
75
75
  stoobly_agent/app/cli/scaffold/config.py,sha256=HZU5tkvr3dkPr4JMXZtrJlu2wxxO-134Em6jReFFcq0,688
76
- stoobly_agent/app/cli/scaffold/constants.py,sha256=ULSzqjNdrWP_-feyqpwPE-Pdt7JVDerZeZoFTpvewHY,2555
76
+ stoobly_agent/app/cli/scaffold/constants.py,sha256=8d0YTcPp78mIVOjLeueJkWU6XzC_KxB8ZaU2B-LuDFs,2652
77
77
  stoobly_agent/app/cli/scaffold/containerized_app.py,sha256=dAjn4RwcZV3aEL0POUmrbF_DC-r9h6s1zx7gT2t45v0,175
78
78
  stoobly_agent/app/cli/scaffold/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  stoobly_agent/app/cli/scaffold/docker/app_builder.py,sha256=7z5pk5JKlRDHx2USxY-WurttLyyUkIVYfl34_u1x9dE,501
@@ -82,7 +82,7 @@ stoobly_agent/app/cli/scaffold/docker/constants.py,sha256=1khQdgTaQ89ykGRNdTQh_e
82
82
  stoobly_agent/app/cli/scaffold/docker/service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
83
  stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py,sha256=ZU7z4bkvdS3OK5O4fJhlA9_PNwnFtZW6t7vNF7V5obQ,1003
84
84
  stoobly_agent/app/cli/scaffold/docker/service/builder.py,sha256=u3_YNuxX8TT4TvhzTqe5b2Zvi9fF7TLWJFepkBSXwhk,5857
85
- stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=8GQ2wUBwb45uSxhzkEMloezJT1hVu8v_3PudWLTU9ig,3910
85
+ stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py,sha256=jbMaa08R2EoZ2esoC4uREfPoBK9b5vyPRuTNOT0QDjE,3949
86
86
  stoobly_agent/app/cli/scaffold/docker/service/types.py,sha256=qB-yYHlu-PZDc0HYgTUvE5bWNpHxaSThC3JUG8okR1k,88
87
87
  stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
88
  stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=VKD9hXbJGRIWHS5IeYXeX0-FQ0F43zG8VmsegL6eYwA,703
@@ -91,13 +91,13 @@ stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py,sha256=bjQ8
91
91
  stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py,sha256=nlDPbyF1hkVItzFH3bk9KlqfDC8usyFZs353-ibqxvA,938
92
92
  stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=DcBnwA8YhE-VdrUiWf2xPcqirEMZEQm3AwssAg9CxLo,1226
93
93
  stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=zD4FEtBMiPEtvEb798LOfFH8sJhTWQYpA9THLq5UMbg,1227
94
- stoobly_agent/app/cli/scaffold/env.py,sha256=e-Ve4p3RUgzFx22B3SIYttvJ_yLuDtA27oDACZ8n-6E,1140
94
+ stoobly_agent/app/cli/scaffold/env.py,sha256=dT33tHoQaUxfsFCYm8kfaAv-qPVrUPmNFQmLnFQhZeQ,1107
95
95
  stoobly_agent/app/cli/scaffold/hosts_file_manager.py,sha256=zNX5wh6zXQ4J2BA0YYdD7_CPqDz02b_ghXsY3oTjjB4,4999
96
96
  stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
97
97
  stoobly_agent/app/cli/scaffold/service.py,sha256=74JwjTRRkk6lo-k9hre1iGztbKa9zDqjPVx3Qgpze-s,699
98
- stoobly_agent/app/cli/scaffold/service_command.py,sha256=NxJakoq1Dy79LEIJ8QSsKEwsofIfN13GN0UGpp9i6qY,1230
99
- stoobly_agent/app/cli/scaffold/service_config.py,sha256=igPvhcP0vENgHCYQhbRJLeltSjv0m5b5fmEq5wMGkd4,3939
100
- stoobly_agent/app/cli/scaffold/service_create_command.py,sha256=1B6TK3JDAjouikCV84WDrX7b0crdPMPIo1caMwhi-L8,2815
98
+ stoobly_agent/app/cli/scaffold/service_command.py,sha256=n5tVp8jIypnNO9uxtaMt33lX5ho0xhV6A53HDN2Yet4,1239
99
+ stoobly_agent/app/cli/scaffold/service_config.py,sha256=WCHy1bZ8-nlVwCWdOWKXrdIVTtjye8XStA1bqvbRgZ0,3959
100
+ stoobly_agent/app/cli/scaffold/service_create_command.py,sha256=nCONco0ducMcX4rsdFe3A-iRiySyUnu-T6bTb312XP0,2824
101
101
  stoobly_agent/app/cli/scaffold/service_delete_command.py,sha256=_nBDQjm8eL62MQpzSCxgUHlW04ZXKG8MDlN1BXxlqww,986
102
102
  stoobly_agent/app/cli/scaffold/service_docker_compose.py,sha256=OMUN1-ujQYIZXxDvS4XBf5C9wGalQULkwOiBBQPZbHY,820
103
103
  stoobly_agent/app/cli/scaffold/service_update_command.py,sha256=oWusBKfvjt4RnK03_V3CJYWrfsCI4_LcR7W12eLXMR4,2579
@@ -105,7 +105,7 @@ stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOW
105
105
  stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=-hdg7RLsoEQXCI9rebIXyO3d8JbpyTxGnhpjaPQfmFk,11514
106
106
  stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=x8C_a0VoO_vUbosp4_6IC1U7Ge9NnUdVKDPpVMtMkeY,171
107
107
  stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=Okk4Q0Fj7Wi5NU58gQfpjpFwAL3RUBJyRe56kteQfcA,158
108
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=bseFu3GsBlH8QEHKiALhz3V8pZr6CBJHM_nU64ZCEsE,8973
108
+ stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=uV-xqit3fz3tfcU6sbBOxK0Zon11v-fydfuYLSxS7jQ,9164
109
109
  stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=6tFqXh3ine8vaD0FCL5TMoY5NjKx2wLUR8XpW3tJtew,245
110
110
  stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.networks.yml,sha256=I4PbJpQjFHb5IbAUWNvYM6okDEtmwtKFDQg-yog05WM,141
111
111
  stoobly_agent/app/cli/scaffold/templates/app/Makefile,sha256=TEmPG7Bf0KZOnmfsgdzza3UdwcVMmM5Lj1YdLc4cgjA,79
@@ -138,7 +138,7 @@ stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure,sha25
138
138
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/init,sha256=YxWVVQMEdGarcMtBcE1Sj2kdLgUgwY9kXp3MjPsVcmg,46
139
139
  stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/docker-compose.yml,sha256=JpWLQrW69EatLD3HPHpmi3Y6n7cjlrUgekYVbJ4ngSY,523
140
140
  stoobly_agent/app/cli/scaffold/templates/app/gateway/.config.yml,sha256=XnLQZMzzMMIwVycjyPN5QXsmRztkTFAna1kIHYuDfJQ,19
141
- stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.template.yml,sha256=0A08JbYpa7LGv9SphRwCgkvy5y4_T74LoWiV-NyCmyA,241
141
+ stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.template.yml,sha256=QipWNy2mEbJuceiNfRbv6L3ARk1lMkD1gE4l_Z0PrPU,246
142
142
  stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml,sha256=CDL5x18-ues4F_ujqpNef_zJTG6PyDOkQ11cIHBOW5k,136
143
143
  stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml,sha256=eyLH2h33Peunus8M1sUKL9AALCG2ABhV_heiJKhvgwo,138
144
144
  stoobly_agent/app/cli/scaffold/templates/app/gateway/test/.docker-compose.test.yml,sha256=oJO6i0lsuQaQeIH80yoPZo3Vs0LzUAH2WRl853yLq6g,136
@@ -196,15 +196,16 @@ stoobly_agent/app/cli/scaffold/templates/workflow/test/public/.gitignore,sha256=
196
196
  stoobly_agent/app/cli/scaffold/validate_command.py,sha256=BEDcF5qPDbMjWe-SCAr5jxcrVMZlV3ciLa89TDDENmM,3787
197
197
  stoobly_agent/app/cli/scaffold/validate_exceptions.py,sha256=Jtjl4OkbbSRWm0hy7Kf_50zcFh2J324HhNcnwJKH_Oc,54
198
198
  stoobly_agent/app/cli/scaffold/workflow.py,sha256=KlbWT9CIo9EpZxKU1WVZtmodhxK7CpmLUHPNk4Mh6DA,1570
199
- stoobly_agent/app/cli/scaffold/workflow_command.py,sha256=eI9I5LLgO0U3b46QhHusy-4BV2zUDVai6jErcluYCRI,3344
199
+ stoobly_agent/app/cli/scaffold/workflow_command.py,sha256=sGe5uVr34P6IZ0qfCTgM8sQ7LkvdvymM2LmBzXpw1ZY,3353
200
200
  stoobly_agent/app/cli/scaffold/workflow_config.py,sha256=ghnbcnCyb6ECdylUJCAJ6A8ulzaFY9bu7RvRuYeiRkk,901
201
201
  stoobly_agent/app/cli/scaffold/workflow_copy_command.py,sha256=wi8qHH_M2e6jXIPuumx65Zd4Zt7hTBb6b3Z4vt4xYeQ,1320
202
202
  stoobly_agent/app/cli/scaffold/workflow_create_command.py,sha256=-fwsr6_LvGT8BbBWdGY3Qd8cSQhBOSJiMr1r8s2R86w,3390
203
- stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=x8V5pJmIiklD3f2q2-qq-CORf4YaXYq_r2JpR2MmSwk,416
203
+ stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=shPjoX1SWe7K6pGpZvw2fPVHWd6j_azTe58jvOjGUns,607
204
204
  stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=Bke4lMOMxuDUFuAx9nlXHbKgYMO4KAg9ASHvjz4aVWc,1372
205
- stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=stKIWnLCfpX39iobHYp4NjFgR2oFTBmyCgYUIcc22_A,10896
205
+ stoobly_agent/app/cli/scaffold/workflow_namesapce.py,sha256=VNaZrcqMMeqrzpPGhD9-oaZems1k0ebRc6wR74EvA8c,1170
206
+ stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=H9s0F8LaoB7ZLUVXWP6M6yPLYk5IE4JD8qgGXntwtMc,11138
206
207
  stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=Uo_yo6rVR1ZR7xpvsQvlH48AyMBVLRupd4G-bRjzm_Q,5584
207
- stoobly_agent/app/cli/scaffold_cli.py,sha256=ovhlabUDEfqkuRGIVzmxdTHwkpnTceYUgzYw-2kVi0I,32367
208
+ stoobly_agent/app/cli/scaffold_cli.py,sha256=F93zXk9WNRLfiDJ-9MpMB4My7gAJJVHDvV_fKPLsxdA,32896
208
209
  stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
209
210
  stoobly_agent/app/cli/snapshot_cli.py,sha256=1Dw5JgDlmG6vctrawIRO7CdB73vAQk_wRBnPG2lVOrQ,11929
210
211
  stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
@@ -330,7 +331,7 @@ stoobly_agent/app/proxy/mitmproxy/response_facade.py,sha256=0wCSzUULUhDDV93QXUgz
330
331
  stoobly_agent/app/proxy/mock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
331
332
  stoobly_agent/app/proxy/mock/context.py,sha256=vDo5_3WBL73mVFnsmQWvcxvPg5nWtRJbigSrE3zGc-o,794
332
333
  stoobly_agent/app/proxy/mock/custom_not_found_response_builder.py,sha256=0KWB3KFxVrnJOKDaYxm5eoJEccw7IpJZRyUvBX61-8k,697
333
- stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=sVw8l916ouEBj2ViDtHFjgmEA4tLmZGvBfw8wD1Ab-c,4132
334
+ stoobly_agent/app/proxy/mock/eval_fixtures_service.py,sha256=J5CRp_RePD4cHuy0gtXqhA_8GJqXZL0NC0alT9TkjfU,5225
334
335
  stoobly_agent/app/proxy/mock/eval_request_service.py,sha256=A1tcE3wmrC1HwLpz0aRuRw-Nucn0dyHD_yHw5BeQEJU,8146
335
336
  stoobly_agent/app/proxy/mock/hashed_request_decorator.py,sha256=h1ma90fdaYI9LBWpMWMqWBz-RjNwI628O4VuS_uUBX4,5061
336
337
  stoobly_agent/app/proxy/mock/ignored_components_response_builder.py,sha256=E32_E1eSdmPn2SeM_e1jWnqu4xh5w_SnmOs32Shx99E,501
@@ -717,7 +718,7 @@ stoobly_agent/test/app/models/schemas/.stoobly/settings.yml,sha256=vLwMjweKOdod6
717
718
  stoobly_agent/test/app/models/schemas/.stoobly/tmp/options.json,sha256=OTRzarwus48CTrItedXCrgQttJHSEZonEYc7R_knvYg,2212
718
719
  stoobly_agent/test/app/models/schemas/request_test.py,sha256=9SF43KXbjO-vMr2uObPJlyLeop_JQstl6Jrh0M1A70c,1116
719
720
  stoobly_agent/test/app/proxy/mitmproxy/request_facade_test.py,sha256=sXlzjKVvpORJ8fxEs5Qfc4IC0itTWxqDIzMujGjlW7I,5765
720
- stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py,sha256=LTn7iWtHyNVqezRfM2Uf_BiK4vFnS3hgoaIdrzjkcPk,5739
721
+ stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py,sha256=bPKabyNUEiOV_F3RG6tiLmfc71qEw5SiUfhhdmphUxA,6333
721
722
  stoobly_agent/test/app/proxy/replay/body_parser_service_test.py,sha256=MTC4a3QxrptHzroHAfgrCEzCZ3Ur0Ijyj9_3k-bG0jQ,1228
722
723
  stoobly_agent/test/app/proxy/replay/rewrite_params_service_test.py,sha256=4KVaP48KjCeoZKqY3IdrFAP5Pnb3jO86k8L7ffvz2ZI,3770
723
724
  stoobly_agent/test/app/proxy/replay/trace_context_test.py,sha256=y0oBNC89sp3BG8biOBTiaTopk1LtqjThlA4d6BwAHzM,14462
@@ -752,8 +753,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
752
753
  stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
753
754
  stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
754
755
  stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
755
- stoobly_agent-1.9.10.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
756
- stoobly_agent-1.9.10.dist-info/METADATA,sha256=V2dDSCdSEvRueylouQW2K4-4_TNU6Q74sqWplOHzWs0,3088
757
- stoobly_agent-1.9.10.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
758
- stoobly_agent-1.9.10.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
759
- stoobly_agent-1.9.10.dist-info/RECORD,,
756
+ stoobly_agent-1.9.12.dist-info/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
757
+ stoobly_agent-1.9.12.dist-info/METADATA,sha256=NZ_3l89VkM5yyJDF7zXVnpiVKWVZeRXdzT54Km0tj8I,3088
758
+ stoobly_agent-1.9.12.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
759
+ stoobly_agent-1.9.12.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
760
+ stoobly_agent-1.9.12.dist-info/RECORD,,