stoobly-agent 1.10.0__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.
Files changed (173) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/__main__.py +10 -0
  3. stoobly_agent/app/api/application_http_request_handler.py +5 -2
  4. stoobly_agent/app/cli/ca_cert_cli.py +9 -5
  5. stoobly_agent/app/cli/helpers/replay_facade.py +2 -2
  6. stoobly_agent/app/cli/intercept_cli.py +5 -5
  7. stoobly_agent/app/cli/request_cli.py +2 -2
  8. stoobly_agent/app/cli/scaffold/app.py +14 -5
  9. stoobly_agent/app/cli/scaffold/app_command.py +0 -4
  10. stoobly_agent/app/cli/scaffold/app_config.py +49 -2
  11. stoobly_agent/app/cli/scaffold/app_create_command.py +145 -76
  12. stoobly_agent/app/cli/scaffold/constants.py +9 -4
  13. stoobly_agent/app/cli/scaffold/docker/constants.py +3 -1
  14. stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +4 -4
  15. stoobly_agent/app/cli/scaffold/docker/service/builder.py +31 -54
  16. stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py +3 -0
  17. stoobly_agent/app/cli/scaffold/docker/template_files.py +112 -0
  18. stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +1 -1
  19. stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +30 -47
  20. stoobly_agent/app/cli/scaffold/docker/workflow/command_decorator.py +3 -2
  21. stoobly_agent/app/cli/scaffold/docker/workflow/detached_decorator.py +1 -1
  22. stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py +2 -3
  23. stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py +1 -1
  24. stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +1 -1
  25. stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +1 -1
  26. stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py +423 -0
  27. stoobly_agent/app/cli/scaffold/local/__init__.py +0 -0
  28. stoobly_agent/app/cli/scaffold/local/service/__init__.py +0 -0
  29. stoobly_agent/app/cli/scaffold/local/service/builder.py +72 -0
  30. stoobly_agent/app/cli/scaffold/local/workflow/__init__.py +0 -0
  31. stoobly_agent/app/cli/scaffold/local/workflow/builder.py +35 -0
  32. stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +339 -0
  33. stoobly_agent/app/cli/scaffold/service_command.py +9 -1
  34. stoobly_agent/app/cli/scaffold/service_config.py +9 -25
  35. stoobly_agent/app/cli/scaffold/service_create_command.py +18 -6
  36. stoobly_agent/app/cli/scaffold/service_docker_compose.py +3 -3
  37. stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +10 -7
  38. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +2 -2
  39. stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +4 -4
  40. stoobly_agent/app/cli/scaffold/templates/app/build/mock/configure +3 -0
  41. stoobly_agent/app/cli/scaffold/templates/app/build/record/configure +28 -0
  42. stoobly_agent/app/cli/scaffold/templates/app/build/test/configure +3 -0
  43. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +4 -4
  44. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/configure +3 -0
  45. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/run +3 -0
  46. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/configure +3 -0
  47. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/run +3 -0
  48. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/configure +3 -0
  49. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/run +3 -0
  50. stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.configure +5 -1
  51. stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.init +5 -1
  52. stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.run +14 -0
  53. stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.configure +5 -1
  54. stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.init +5 -1
  55. stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.run +14 -0
  56. stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.configure +5 -1
  57. stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.init +5 -1
  58. stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.run +14 -0
  59. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.configure +5 -1
  60. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.init +5 -1
  61. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.run +19 -0
  62. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.configure +5 -1
  63. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.init +5 -1
  64. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.run +19 -0
  65. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.configure +5 -1
  66. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.init +5 -1
  67. stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.run +19 -0
  68. stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up +0 -1
  69. stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.configure +5 -1
  70. stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.init +5 -1
  71. stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.run +14 -0
  72. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure +25 -1
  73. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.init +5 -1
  74. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.run +14 -0
  75. stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.configure +5 -1
  76. stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.init +5 -1
  77. stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.run +14 -0
  78. stoobly_agent/app/cli/scaffold/templates/constants.py +35 -19
  79. stoobly_agent/app/cli/scaffold/templates/factory.py +34 -18
  80. stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run +21 -0
  81. stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run +21 -0
  82. stoobly_agent/app/cli/scaffold/templates/workflow/mock/configure +5 -0
  83. stoobly_agent/app/cli/scaffold/templates/workflow/mock/run +3 -0
  84. stoobly_agent/app/cli/scaffold/templates/workflow/record/configure +21 -0
  85. stoobly_agent/app/cli/scaffold/templates/workflow/record/run +3 -0
  86. stoobly_agent/app/cli/scaffold/templates/workflow/test/configure +5 -0
  87. stoobly_agent/app/cli/scaffold/templates/workflow/test/run +3 -0
  88. stoobly_agent/app/cli/scaffold/workflow_command.py +18 -4
  89. stoobly_agent/app/cli/scaffold/workflow_copy_command.py +5 -4
  90. stoobly_agent/app/cli/scaffold/workflow_create_command.py +31 -29
  91. stoobly_agent/app/cli/scaffold/workflow_run_command.py +18 -151
  92. stoobly_agent/app/cli/scaffold_cli.py +134 -182
  93. stoobly_agent/app/cli/scenario_cli.py +2 -2
  94. stoobly_agent/app/cli/types/test.py +2 -2
  95. stoobly_agent/app/cli/types/workflow_run_command.py +52 -3
  96. stoobly_agent/app/proxy/handle_mock_service.py +1 -1
  97. stoobly_agent/app/proxy/intercept_settings.py +6 -26
  98. stoobly_agent/app/proxy/mock/eval_fixtures_service.py +177 -27
  99. stoobly_agent/app/proxy/mock/types/__init__.py +22 -1
  100. stoobly_agent/app/proxy/record/upload_request_service.py +3 -6
  101. stoobly_agent/app/proxy/replay/body_parser_service.py +8 -5
  102. stoobly_agent/app/proxy/replay/multipart.py +15 -13
  103. stoobly_agent/app/proxy/replay/replay_request_service.py +2 -2
  104. stoobly_agent/app/proxy/run.py +3 -0
  105. stoobly_agent/app/proxy/test/context.py +0 -4
  106. stoobly_agent/app/proxy/test/context_abc.py +0 -5
  107. stoobly_agent/app/proxy/utils/publish_change_service.py +20 -23
  108. stoobly_agent/app/settings/__init__.py +10 -7
  109. stoobly_agent/cli.py +61 -16
  110. stoobly_agent/config/data_dir.py +1 -8
  111. stoobly_agent/public/12-es2015.618ecfd5f735b801b50f.js +1 -0
  112. stoobly_agent/public/12-es5.618ecfd5f735b801b50f.js +1 -0
  113. stoobly_agent/public/index.html +1 -1
  114. stoobly_agent/public/main-es2015.5a9aa16433404c3f423a.js +1 -0
  115. stoobly_agent/public/main-es5.5a9aa16433404c3f423a.js +1 -0
  116. stoobly_agent/public/runtime-es2015.77bcd31efed9e5d5d431.js +1 -0
  117. stoobly_agent/public/runtime-es5.77bcd31efed9e5d5d431.js +1 -0
  118. stoobly_agent/test/app/cli/intercept/intercept_configure_test.py +17 -6
  119. stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py +177 -0
  120. stoobly_agent/test/app/cli/scaffold/{cli_test.py → docker/cli_test.py} +4 -11
  121. stoobly_agent/test/app/cli/scaffold/{e2e_test.py → docker/e2e_test.py} +42 -27
  122. stoobly_agent/test/app/cli/scaffold/local/__init__.py +0 -0
  123. stoobly_agent/test/app/cli/scaffold/{cli_invoker.py → local/cli_invoker.py} +38 -32
  124. stoobly_agent/test/app/cli/scaffold/local/e2e_test.py +342 -0
  125. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  126. stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +903 -2
  127. stoobly_agent/test/app/proxy/replay/body_parser_service_test.py +95 -3
  128. stoobly_agent/test/config/data_dir_test.py +2 -7
  129. stoobly_agent/test/test_helper.py +16 -5
  130. {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/METADATA +4 -2
  131. {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/RECORD +157 -129
  132. {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/WHEEL +1 -1
  133. stoobly_agent/app/cli/helpers/shell.py +0 -26
  134. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure +0 -3
  135. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/configure +0 -3
  136. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure +0 -3
  137. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure +0 -3
  138. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure +0 -3
  139. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure +0 -3
  140. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/configure +0 -13
  141. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure +0 -47
  142. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure +0 -13
  143. stoobly_agent/public/12-es2015.be58ed0ef449008b932e.js +0 -1
  144. stoobly_agent/public/12-es5.be58ed0ef449008b932e.js +0 -1
  145. stoobly_agent/public/main-es2015.089b46f303768fbe864f.js +0 -1
  146. stoobly_agent/public/main-es5.089b46f303768fbe864f.js +0 -1
  147. stoobly_agent/public/runtime-es2015.f8c814b38b27708e91c1.js +0 -1
  148. stoobly_agent/public/runtime-es5.f8c814b38b27708e91c1.js +0 -1
  149. /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
  150. /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{bin/init → init} +0 -0
  151. /stoobly_agent/app/cli/scaffold/templates/app/build/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
  152. /stoobly_agent/app/cli/scaffold/templates/app/build/record/{bin/init → init} +0 -0
  153. /stoobly_agent/app/cli/scaffold/templates/app/build/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
  154. /stoobly_agent/app/cli/scaffold/templates/app/build/test/{bin/init → init} +0 -0
  155. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
  156. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{bin/init → init} +0 -0
  157. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
  158. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{bin/init → init} +0 -0
  159. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
  160. /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{bin/init → init} +0 -0
  161. /stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
  162. /stoobly_agent/app/cli/scaffold/templates/app/gateway/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
  163. /stoobly_agent/app/cli/scaffold/templates/app/gateway/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
  164. /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/{.docker-compose.exec.yml → .docker-compose.yml} +0 -0
  165. /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
  166. /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
  167. /stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
  168. /stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
  169. /stoobly_agent/app/cli/scaffold/templates/workflow/mock/{bin/init → init} +0 -0
  170. /stoobly_agent/app/cli/scaffold/templates/workflow/record/{bin/init → init} +0 -0
  171. /stoobly_agent/app/cli/scaffold/templates/workflow/test/{bin/init → init} +0 -0
  172. {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/entry_points.txt +0 -0
  173. {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,339 @@
1
+ import os
2
+ import pdb
3
+ import signal
4
+ import subprocess
5
+ import sys
6
+
7
+ from typing import Optional, List
8
+
9
+ from stoobly_agent.app.cli.scaffold.constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT
10
+ from stoobly_agent.app.cli.scaffold.docker.constants import PLUGIN_CONTAINER_SERVICE_TEMPLATE
11
+ from stoobly_agent.app.cli.scaffold.templates.constants import CORE_BUILD_SERVICE_NAME, CORE_ENTRYPOINT_SERVICE_NAME, CUSTOM_CONFIGURE, CUSTOM_INIT, CUSTOM_RUN, MAINTAINED_CONFIGURE, MAINTAINED_INIT, MAINTAINED_RUN
12
+ from stoobly_agent.app.cli.scaffold.workflow_run_command import WorkflowRunCommand
13
+ from stoobly_agent.app.cli.types.workflow_run_command import WorkflowUpOptions, WorkflowDownOptions, WorkflowLogsOptions
14
+ from stoobly_agent.lib.logger import Logger
15
+
16
+ LOG_ID = 'LocalWorkflowRunCommand'
17
+
18
+ class LocalWorkflowRunCommand(WorkflowRunCommand):
19
+ """Local workflow run command that executes stoobly-agent run directly."""
20
+
21
+ def __init__(self, app, services=None, script=None, **kwargs):
22
+ super().__init__(app, **kwargs)
23
+
24
+ self.services = services or []
25
+ self.script = script
26
+
27
+ self._log_file_path = None
28
+ self._pid_file_path = None
29
+
30
+ @property
31
+ def log_file_path(self):
32
+ """Get the path to the PID file for this workflow."""
33
+ if not self._log_file_path:
34
+ self._log_file_path = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.log")
35
+ return self._log_file_path
36
+
37
+ @property
38
+ def pid_file_path(self):
39
+ """Get the path to the PID file for this workflow."""
40
+ if not self._pid_file_path:
41
+ self._pid_file_path = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.pid")
42
+ return self._pid_file_path
43
+
44
+ def _write_pid(self, pid: int):
45
+ """Write the process PID to the PID file."""
46
+ os.makedirs(os.path.dirname(self.pid_file_path), exist_ok=True)
47
+ with open(self.pid_file_path, 'w') as f:
48
+ f.write(str(pid))
49
+
50
+ def _read_pid(self) -> Optional[int]:
51
+ """Read the process PID from the PID file."""
52
+ if not os.path.exists(self.pid_file_path):
53
+ return None
54
+
55
+ try:
56
+ with open(self.pid_file_path, 'r') as f:
57
+ return int(f.read().strip())
58
+ except (ValueError, IOError):
59
+ return None
60
+
61
+ def _kill_process(self, pid: int, signal_type=signal.SIGTERM):
62
+ """Kill a process by PID."""
63
+ try:
64
+ os.kill(pid, signal_type)
65
+ return True
66
+ except (OSError, ProcessLookupError):
67
+ return False
68
+
69
+ def _is_process_running(self, pid: int) -> bool:
70
+ """Check if a process is still running."""
71
+ try:
72
+ os.kill(pid, 0) # Signal 0 doesn't kill the process, just checks if it exists
73
+ return True
74
+ except (OSError, ProcessLookupError):
75
+ return False
76
+
77
+ def exec_service_script(self, service_name: str, step_script_path: str, args: List[str], cwd = None):
78
+ workflow_path = cwd or os.path.join(self.app.scaffold_namespace_path, service_name, self.workflow_name)
79
+
80
+ # Change directory to workflow path
81
+ command = [step_script_path] + args
82
+ if self.script:
83
+ command = [f"cd \"{workflow_path}\";"] + command
84
+
85
+ # Write the command to self.script_path
86
+ if self.script:
87
+ print(' '.join(command), file=self.script)
88
+
89
+ if self.dry_run:
90
+ print(' '.join(command))
91
+ else:
92
+ result = subprocess.run(['sh', '-c', ' '.join(command)], cwd=workflow_path)
93
+ if result.returncode != 0:
94
+ sys.exit(1)
95
+
96
+ def service_up(self, **options: WorkflowUpOptions):
97
+ print_service_header = options.get('print_service_header')
98
+ service_name = self.service_name
99
+ workflow_template = self.workflow_template
100
+
101
+ if print_service_header:
102
+ print_service_header(service_name)
103
+
104
+ self.write_env(**options)
105
+
106
+ # If service is build or entrypoint, use path in templates/build/services/SERVICE_NAME/.init
107
+ if service_name in [CORE_BUILD_SERVICE_NAME, CORE_ENTRYPOINT_SERVICE_NAME]:
108
+ init_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_INIT)
109
+ configure_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_CONFIGURE)
110
+ run_script_path = os.path.join(self.workflow_path, MAINTAINED_RUN)
111
+
112
+ if not os.path.exists(run_script_path):
113
+ run_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_RUN)
114
+ else:
115
+ # Absolute path to workflow .init script
116
+ # e.g. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.init
117
+ init_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_INIT)
118
+
119
+ # Absolute path to workflow .configure script
120
+ # e.g. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure
121
+ configure_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_CONFIGURE)
122
+
123
+ run_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_RUN)
124
+
125
+ self.exec_service_script(service_name, init_script_path, [CUSTOM_INIT])
126
+ self.exec_service_script(service_name, configure_script_path, [CUSTOM_CONFIGURE])
127
+ self.exec_service_script(service_name, run_script_path, [CUSTOM_RUN], cwd=os.getcwd())
128
+
129
+ def up(self, **options: WorkflowUpOptions):
130
+ """Start the workflow using local stoobly-agent run."""
131
+ detached = options.get('detached', False)
132
+
133
+ commands = self.workflow_service_commands(**options)
134
+
135
+ # iterate through each service in the workflow
136
+ public_directory_paths = []
137
+ response_fixtures_paths = []
138
+ for command in commands:
139
+ url = command.service_config.url
140
+ if url:
141
+ if os.path.exists(command.public_dir_path):
142
+ public_directory_paths.append('--public-directory-path')
143
+ public_directory_paths.append(f"{command.public_dir_path}:{url}")
144
+
145
+ if os.path.exists(command.response_fixtures_path):
146
+ response_fixtures_paths.append('--response-fixtures-path')
147
+ response_fixtures_paths.append(f"{command.response_fixtures_path}:{url}")
148
+
149
+ # Check if PID file already exists
150
+ if os.path.exists(self.pid_file_path):
151
+ pid = self._read_pid()
152
+ if pid and self._is_process_running(pid):
153
+ Logger.instance(LOG_ID).error(f"Workflow {self.workflow_name} is already running with PID: {pid}")
154
+ Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {self.workflow_name}` to stop it first")
155
+ sys.exit(1)
156
+ else:
157
+ # PID file exists but process is not running, clean it up
158
+ os.remove(self.pid_file_path)
159
+
160
+ for command in commands:
161
+ command.service_up(**options)
162
+
163
+ # If second from last command, run up_command i.e. right before entrypoint
164
+ if command == commands[-2]:
165
+ self.__up_command(public_directory_paths, response_fixtures_paths, **options)
166
+
167
+ def down(self, **options: WorkflowDownOptions):
168
+ """Stop the workflow by killing the local process."""
169
+
170
+ pid = self._read_pid()
171
+ if not pid:
172
+ Logger.instance(LOG_ID).warning(f"No PID file found for {self.workflow_name}")
173
+ return
174
+
175
+ if not self._is_process_running(pid):
176
+ Logger.instance(LOG_ID).info(f"Process {pid} for {self.workflow_name} is not running")
177
+ # Clean up PID file
178
+ if os.path.exists(self.pid_file_path):
179
+ os.remove(self.pid_file_path)
180
+ return
181
+
182
+ # Kill the process
183
+ if self.script:
184
+ self.__dry_run_down(pid, self.script)
185
+
186
+ if self.dry_run:
187
+ self.__dry_run_down(pid, sys.stdout)
188
+ else:
189
+ try:
190
+ # Try graceful shutdown first with SIGTERM
191
+ Logger.instance(LOG_ID).info(f"Sending SIGTERM to process {pid} for {self.workflow_name}")
192
+ self._kill_process(pid, signal.SIGTERM)
193
+
194
+ # Wait a bit for graceful shutdown
195
+ import time
196
+ time.sleep(2)
197
+
198
+ # Check if process still exists
199
+ if self._is_process_running(pid):
200
+ Logger.instance(LOG_ID).info(f"Process {pid} still running, sending SIGKILL")
201
+ self._kill_process(pid, signal.SIGKILL)
202
+
203
+ # Wait a bit more for SIGKILL to take effect
204
+ time.sleep(1)
205
+
206
+ # Final check
207
+ if self._is_process_running(pid):
208
+ Logger.instance(LOG_ID).warning(f"Process {pid} may still be running after SIGKILL")
209
+ else:
210
+ Logger.instance(LOG_ID).info(f"Successfully stopped process {pid} for {self.workflow_name}")
211
+ else:
212
+ Logger.instance(LOG_ID).info(f"Successfully stopped process {pid} for {self.workflow_name}")
213
+
214
+ # Clean up PID file
215
+ if os.path.exists(self.pid_file_path):
216
+ os.remove(self.pid_file_path)
217
+
218
+ except Exception as e:
219
+ Logger.instance(LOG_ID).error(f"Failed to stop {self.workflow_name}: {e}")
220
+
221
+ def logs(self, **options: WorkflowLogsOptions):
222
+ """Show logs for the local workflow process."""
223
+ follow = options.get('follow', False)
224
+
225
+ pid = self._read_pid()
226
+ if not pid:
227
+ Logger.instance(LOG_ID).warning(f"No PID file found for {self.workflow_name}")
228
+ return
229
+
230
+ # Build log command
231
+ log_file = f"{self.log_file_path}"
232
+ if self.script:
233
+ self.__dry_run_logs(log_file, self.script, follow)
234
+
235
+ if self.dry_run:
236
+ self.__dry_run_logs(log_file, sys.stdout, follow)
237
+ else:
238
+ try:
239
+ if follow:
240
+ subprocess.run(['tail', '-f', log_file])
241
+ else:
242
+ subprocess.run(['cat', log_file])
243
+ except subprocess.CalledProcessError as e:
244
+ Logger.instance(LOG_ID).error(f"Failed to show logs for {self.workflow_name}: {e}")
245
+
246
+ def status(self):
247
+ """Check the status of the local workflow process."""
248
+ pid = self._read_pid()
249
+ if not pid:
250
+ return "not running"
251
+
252
+ if self._is_process_running(pid):
253
+ return f"running (PID: {pid})"
254
+ else:
255
+ return "not running (stale PID file)"
256
+
257
+ def workflow_service_commands(self, **options: WorkflowUpOptions):
258
+ commands = list(map(lambda service_name: LocalWorkflowRunCommand(self.app, service_name=service_name, **options), self.services))
259
+ commands.sort(key=lambda command: command.service_config.priority)
260
+ return commands
261
+
262
+ def __dry_run_down(self, pid: int, output_file: str):
263
+ print(f"# Stop {self.workflow_name} (PID: {pid})", file=output_file)
264
+ print(f"kill {pid} || true", file=output_file)
265
+ print("sleep 1", file=output_file)
266
+ print(f"kill -0 {pid} 2>/dev/null && kill {pid} || true", file=output_file)
267
+ print(f"rm -f {self.pid_file_path}", file=output_file)
268
+
269
+ def __dry_run_logs(self, log_file: str, output_file: str, follow: bool):
270
+ print(f"# Show logs for {self.workflow_name}", file=output_file)
271
+ if follow:
272
+ print(f"tail -f {log_file}", file=output_file)
273
+ else:
274
+ print(f"cat {log_file}", file=output_file)
275
+
276
+ def __up_command(self, public_directory_paths: List[str], response_fixtures_paths: List[str], **options: WorkflowUpOptions):
277
+ # Build the stoobly-agent run command
278
+ command = ['stoobly-agent', 'run']
279
+
280
+ # Add log level if provided
281
+ if options.get('log_level'):
282
+ command.extend(['--log-level', options['log_level']])
283
+
284
+ # Use the PID file path as the detached output file
285
+ command.extend(['--detached', self.log_file_path])
286
+
287
+ command.extend(['--proxy-port', f"{self.app_config.proxy_port}"])
288
+ command.extend(['--ui-port', f"{self.app_config.ui_port}"])
289
+
290
+ if public_directory_paths:
291
+ command.extend(public_directory_paths)
292
+
293
+ if response_fixtures_paths:
294
+ command.extend(response_fixtures_paths)
295
+
296
+ # Convert command to string
297
+ command_str = ' '.join(command)
298
+
299
+ # Run in background using the main run command's --detached option
300
+ if self.script:
301
+ # Write to script for detached execution
302
+ script_lines = [
303
+ f"# Start {self.workflow_name} in background using --detached",
304
+ f"{command_str} > {self.pid_file_path}",
305
+ f"echo \"Started {self.workflow_name} with PID: $(cat {self.pid_file_path})\""
306
+ ]
307
+ for line in script_lines:
308
+ print(line, file=self.script)
309
+
310
+ if self.dry_run:
311
+ print(command_str)
312
+ else:
313
+ # Execute directly
314
+ try:
315
+ # Run the command with --detached option
316
+ result = subprocess.run(
317
+ command,
318
+ capture_output=True,
319
+ text=True,
320
+ check=True
321
+ )
322
+
323
+ if result.returncode != 0:
324
+ Logger.instance(LOG_ID).error(f"Failed to start agent, run `stoobly-agent workflow logs {self.workflow_name}` to see the error")
325
+ sys.exit(1)
326
+
327
+ # The --detached option prints the PID to stdout
328
+ pid = int(result.stdout.strip())
329
+
330
+ # Write PID to file
331
+ self._write_pid(pid)
332
+
333
+ Logger.instance(LOG_ID).info(f"Started {self.workflow_name} with PID: {pid}")
334
+ except subprocess.CalledProcessError as e:
335
+ Logger.instance(LOG_ID).error(f"Failed to start {self.workflow_name}: {e}")
336
+ return None
337
+ except ValueError as e:
338
+ Logger.instance(LOG_ID).error(f"Failed to parse PID from output: {e}")
339
+ return None
@@ -12,10 +12,14 @@ class ServiceCommand(AppCommand):
12
12
  super().__init__(app)
13
13
  self.__service_name = kwargs.get('service_name')
14
14
 
15
- self.__config = ServiceConfig(self.service_path, **kwargs)
15
+ if kwargs.get('service_name'):
16
+ self.__config = ServiceConfig(self.service_path, **kwargs)
16
17
 
17
18
  @property
18
19
  def service_config(self):
20
+ if not self.service_name:
21
+ raise Exception("Service name is required")
22
+
19
23
  return self.__config
20
24
 
21
25
  @service_config.setter
@@ -52,6 +56,10 @@ class ServiceCommand(AppCommand):
52
56
  self.service_name,
53
57
  )
54
58
 
59
+ @property
60
+ def service_templates_root_dir(self):
61
+ return os.path.join(self.templates_root_dir, 'build', 'services')
62
+
55
63
  def config(self, _c: dict):
56
64
  _config = self.app_config.read()
57
65
  _config.update(self.service_config.read())
@@ -1,5 +1,6 @@
1
1
  # Wraps the .config.yml file in the service folder
2
2
  import hashlib
3
+ import os
3
4
  import pdb
4
5
  import re
5
6
 
@@ -12,7 +13,6 @@ from .constants import (
12
13
  SERVICE_NAME_ENV,
13
14
  SERVICE_PRIORITY_ENV,
14
15
  SERVICE_PORT_ENV,
15
- SERVICE_PROXY_MODE_ENV,
16
16
  SERVICE_SCHEME_ENV,
17
17
  SERVICE_UPSTREAM_HOSTNAME_ENV,
18
18
  SERVICE_UPSTREAM_PORT_ENV,
@@ -30,7 +30,6 @@ class ServiceConfig(Config):
30
30
  self.__name = None
31
31
  self.__port = None
32
32
  self.__priority = None
33
- self.__proxy_mode = None
34
33
  self.__scheme = None
35
34
  self.__upstream_hostname = None
36
35
  self.__upstream_port = None
@@ -58,9 +57,6 @@ class ServiceConfig(Config):
58
57
  if 'priority' in kwargs:
59
58
  self.__priority = kwargs.get('priority')
60
59
 
61
- if 'proxy_mode' in kwargs:
62
- self.__proxy_mode = kwargs.get('proxy_mode')
63
-
64
60
  if 'scheme' in kwargs:
65
61
  self.__scheme = kwargs.get('scheme')
66
62
 
@@ -73,6 +69,10 @@ class ServiceConfig(Config):
73
69
  if 'upstream_scheme' in kwargs:
74
70
  self.__upstream_scheme = kwargs.get('upstream_scheme')
75
71
 
72
+ @property
73
+ def app_config_dir_path(self):
74
+ return os.path.dirname(self.dir)
75
+
76
76
  @property
77
77
  def detached(self) -> bool:
78
78
  return not not self.__detached
@@ -152,22 +152,6 @@ class ServiceConfig(Config):
152
152
  def priority(self, v):
153
153
  self.__priority = v
154
154
 
155
- @property
156
- def proxy_mode(self) -> str:
157
- if self.__proxy_mode:
158
- return (self.__proxy_mode or '').strip()
159
-
160
- if not self.hostname:
161
- return ''
162
-
163
- return 'regular'
164
-
165
- @proxy_mode.setter
166
- def proxy_mode(self, v: str):
167
- if v and v not in ['regular', 'reverse']:
168
- v = 'reverse'
169
- self.__proxy_mode = v
170
-
171
155
  @property
172
156
  def scheme(self):
173
157
  if not self.__scheme and self.__port:
@@ -221,10 +205,13 @@ class ServiceConfig(Config):
221
205
 
222
206
  @upstream_scheme.setter
223
207
  def upstream_scheme(self, v):
224
- self.__scheme = v
208
+ self.__upstream_scheme = v
225
209
 
226
210
  @property
227
211
  def url(self):
212
+ if not self.hostname:
213
+ return ''
214
+
228
215
  _url = f"{self.scheme}://{self.hostname}"
229
216
 
230
217
  if not self.port:
@@ -247,7 +234,6 @@ class ServiceConfig(Config):
247
234
  self.name = config.get(SERVICE_NAME_ENV)
248
235
  self.port = config.get(SERVICE_PORT_ENV)
249
236
  self.priority = config.get(SERVICE_PRIORITY_ENV)
250
- self.proxy_mode = config.get(SERVICE_PROXY_MODE_ENV)
251
237
  self.scheme = config.get(SERVICE_SCHEME_ENV)
252
238
  self.upstream_hostname = config.get(SERVICE_UPSTREAM_HOSTNAME_ENV)
253
239
  self.upstream_port = config.get(SERVICE_UPSTREAM_PORT_ENV)
@@ -261,7 +247,6 @@ class ServiceConfig(Config):
261
247
  'name': self.name,
262
248
  'port': self.port,
263
249
  'priority': self.priority,
264
- 'proxy_mode': self.proxy_mode,
265
250
  'scheme': self.scheme if self.hostname else '',
266
251
  'upstream_hostname': self.upstream_hostname,
267
252
  'upstream_port': self.upstream_port,
@@ -300,6 +285,5 @@ class ServiceConfig(Config):
300
285
 
301
286
  config[SERVICE_DETACHED_ENV] = bool(self.detached)
302
287
  config[SERVICE_ID_ENV] = self.id
303
- config[SERVICE_PROXY_MODE_ENV] = self.proxy_mode
304
288
 
305
289
  super().write(config)
@@ -3,10 +3,12 @@ import pdb
3
3
  import shutil
4
4
 
5
5
  from copy import deepcopy
6
+ from typing import Union
6
7
 
7
8
  from .app import App
8
- from .constants import WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
9
- from .docker.service.builder import ServiceBuilder
9
+ from .constants import RUN_ON_DOCKER, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
10
+ from .local.service.builder import ServiceBuilder
11
+ from .docker.service.builder import DockerServiceBuilder
10
12
  from .docker.workflow.decorators_factory import get_workflow_decorators
11
13
  from .service_command import ServiceCommand
12
14
  from .workflow_create_command import WorkflowCreateCommand
@@ -32,8 +34,18 @@ class ServiceCreateCommand(ServiceCommand):
32
34
  def workflows(self):
33
35
  return self.__workflows
34
36
 
37
+ @property
38
+ def create_docker_files(self):
39
+ """Determine if Docker files should be created based on app config run-on setting."""
40
+ return RUN_ON_DOCKER in self.app_config.run_on
41
+
35
42
  def build(self):
36
- service_builder = ServiceBuilder(self.service_config)
43
+ # Choose builder based on app run_on configuration
44
+ if RUN_ON_DOCKER in self.app_config.run_on:
45
+ service_builder = DockerServiceBuilder(self.service_config)
46
+ else:
47
+ service_builder = ServiceBuilder(self.service_config)
48
+
37
49
  service_builder.with_env(list(self.env_vars))
38
50
  service_decorators = []
39
51
 
@@ -67,19 +79,19 @@ class ServiceCreateCommand(ServiceCommand):
67
79
  if os.path.exists(dest):
68
80
  shutil.rmtree(dest)
69
81
 
70
- def __build_with_mock_workflow(self, service_builder: ServiceBuilder, **kwargs):
82
+ def __build_with_mock_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
71
83
  mock_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_MOCK_TYPE }})
72
84
 
73
85
  workflow_decorators = get_workflow_decorators(WORKFLOW_MOCK_TYPE, self.service_config)
74
86
  mock_workflow.build(service_builder=service_builder, workflow_decorators=workflow_decorators)
75
87
 
76
- def __build_with_record_workflow(self, service_builder: ServiceBuilder, **kwargs):
88
+ def __build_with_record_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
77
89
  record_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_RECORD_TYPE }})
78
90
 
79
91
  workflow_decorators = get_workflow_decorators(WORKFLOW_RECORD_TYPE, self.service_config)
80
92
  record_workflow.build(service_builder=service_builder, workflow_decorators=workflow_decorators)
81
93
 
82
- def __build_with_test_workflow(self, service_builder: ServiceBuilder, **kwargs):
94
+ def __build_with_test_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
83
95
  mock_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_TEST_TYPE }})
84
96
 
85
97
  workflow_decorators = get_workflow_decorators(WORKFLOW_TEST_TYPE, self.service_config)
@@ -1,6 +1,6 @@
1
+ from stoobly_agent.app.cli.scaffold.constants import SERVICES_NAMESPACE
1
2
  from stoobly_agent.config.data_dir import DataDir
2
3
 
3
-
4
4
  class ServiceDockerCompose():
5
5
  def __init__(self, app_dir_path, target_workflow_name, service_name, hostname):
6
6
  self.service_name = service_name
@@ -11,5 +11,5 @@ class ServiceDockerCompose():
11
11
  self.configure_container_name = f"{target_workflow_name}-{service_name}.configure-1"
12
12
 
13
13
  data_dir_path = DataDir.instance(app_dir_path).path
14
- self.docker_compose_path = f"{data_dir_path}/docker/{service_name}/{target_workflow_name}/docker-compose.yml"
15
- self.init_script_path = f"{data_dir_path}/docker/{service_name}/{target_workflow_name}/bin/init"
14
+ self.docker_compose_path = f"{data_dir_path}/{SERVICES_NAMESPACE}/{service_name}/{target_workflow_name}/docker-compose.yml"
15
+ self.init_script_path = f"{data_dir_path}/{SERVICES_NAMESPACE}/{service_name}/{target_workflow_name}/init"
@@ -12,11 +12,12 @@ from docker.models.containers import Container
12
12
 
13
13
  from stoobly_agent.app.cli.scaffold.constants import (
14
14
  PUBLIC_FOLDER_NAME,
15
+ SERVICES_NAMESPACE,
15
16
  STOOBLY_DATA_DIR,
16
17
  WORKFLOW_RECORD_TYPE,
17
18
  WORKFLOW_TEST_TYPE,
18
19
  )
19
- from stoobly_agent.app.cli.scaffold.docker.constants import APP_EGRESS_NETWORK_TEMPLATE
20
+ from stoobly_agent.app.cli.scaffold.docker.constants import APP_INGRESS_NETWORK_TEMPLATE
20
21
  from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
21
22
  from stoobly_agent.app.cli.scaffold.service_command import ServiceCommand
22
23
  from stoobly_agent.app.cli.scaffold.service_docker_compose import ServiceDockerCompose
@@ -118,12 +119,14 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
118
119
  print(f"Validating hostname inside Docker network, url: {url}")
119
120
 
120
121
  # See WorkflowRunCommand for how 'network' is generated
122
+ # Debug command=f"curl -k --max-time {timeout_seconds} {url} --verbose",
121
123
  network = f"{self.workflow_name}.{self.app.network}"
122
124
  timeout_seconds = 1
125
+ http_code_format = '"%{http_code}"'
123
126
  output = self.docker_client.containers.run(
124
127
  image='curlimages/curl:8.11.0',
125
- command=f"curl --max-time {timeout_seconds} {url} --verbose",
126
- network=APP_EGRESS_NETWORK_TEMPLATE.format(network=network),
128
+ command=f"curl -k -s -o /dev/null -w {http_code_format} --max-time {timeout_seconds} {url}",
129
+ network=APP_INGRESS_NETWORK_TEMPLATE.format(network=network),
127
130
  stderr=True,
128
131
  remove=True,
129
132
  )
@@ -131,7 +134,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
131
134
  # Note: 499 error could also mean success because it shows the proxy
132
135
  # connection is working, but we haven't recorded anything yet
133
136
  logs = output.decode('ascii')
134
- if ('200 OK' not in logs) and ('499' not in logs):
137
+ if logs != '200' and logs != '499':
135
138
  raise ScaffoldValidateException(f"Error reaching {url} from inside Docker network")
136
139
 
137
140
  # Check public folder exists in container
@@ -190,14 +193,14 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
190
193
  def validate(self) -> bool:
191
194
  print(f"Validating service: {self.service_name}")
192
195
 
193
- url = f"{self.service_config.scheme}://{self.hostname}"
196
+ url = self.service_config.url
194
197
 
195
198
  if self.service_config.hostname and self.workflow_name not in [WORKFLOW_TEST_TYPE]:
196
199
  self.validate_hostname(self.hostname, self.service_config.port)
197
200
 
198
201
  # Test workflow won't expose services that are detached and have a hostname to the host such as assets.
199
202
  # Need to test connection from inside the Docker network
200
- if self.service_config.hostname and self.workflow_name == WORKFLOW_TEST_TYPE and self.service_config.detached:
203
+ if self.service_config.hostname and self.workflow_name == WORKFLOW_TEST_TYPE:
201
204
  self.validate_internal_hostname(url)
202
205
 
203
206
  self.validate_init_containers(self.service_docker_compose.init_container_name, self.service_docker_compose.configure_container_name)
@@ -231,7 +234,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
231
234
  if self.is_local():
232
235
  print(f"Validating local user defined service: {self.service_name}")
233
236
  # Validate docker-compose path exists
234
- docker_compose_path = f"{self.app_dir_path}/{DATA_DIR_NAME}/docker/{self.service_docker_compose.service_name}/{self.workflow_name}/docker-compose.yml"
237
+ docker_compose_path = f"{self.app_dir_path}/{DATA_DIR_NAME}/{SERVICES_NAMESPACE}/{self.service_docker_compose.service_name}/{self.workflow_name}/docker-compose.yml"
235
238
  destination_path = Path(docker_compose_path)
236
239
 
237
240
  if not destination_path.exists():
@@ -33,10 +33,10 @@ workflow=record
33
33
  workflow_service_options=$(shell echo $$STOOBLY_WORKFLOW_SERVICE_OPTIONS)
34
34
 
35
35
  app_data_dir=$(app_dir)/.stoobly
36
- app_namespace_dir=$(app_data_dir)/docker
36
+ app_namespace_dir=$(app_data_dir)/services
37
37
  app_tmp_dir=$(app_data_dir)/tmp
38
38
  dockerfile_path=$(app_namespace_dir)/.Dockerfile.context
39
- exec_docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.exec.yml
39
+ exec_docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.yml
40
40
  workflow_namespace=$(if $(namespace),$(namespace),$(workflow))
41
41
  workflow_namespace_dir=$(app_tmp_dir)/$(workflow_namespace)
42
42
  workflow_script=.stoobly/tmp/$(workflow_namespace)/run.sh
@@ -2,18 +2,18 @@ services:
2
2
  build.configure_base:
3
3
  command:
4
4
  - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
5
- - bin/configure
5
+ - configure
6
6
  extends:
7
7
  file: ../.docker-compose.base.yml
8
8
  service: context_base
9
- working_dir: /home/stoobly/.stoobly/docker/${SERVICE_NAME}/${WORKFLOW_NAME}
9
+ working_dir: /home/stoobly/.stoobly/services/${SERVICE_NAME}/${WORKFLOW_NAME}
10
10
  build.init_base:
11
11
  command:
12
12
  - ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
13
- - bin/init
13
+ - init
14
14
  extends:
15
15
  file: ../.docker-compose.base.yml
16
16
  service: context_base
17
17
  volumes:
18
18
  - ${APP_DIR}:/app
19
- working_dir: /home/stoobly/.stoobly/docker/${SERVICE_NAME}/${WORKFLOW_NAME}
19
+ working_dir: /home/stoobly/.stoobly/services/${SERVICE_NAME}/${WORKFLOW_NAME}
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ # Add custom Stoobly configuration here, to learn more: https://docs.stoobly.com/core-concepts/agent/proxy-settings
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+
3
+ # Add custom Stoobly configuration here, to learn more: https://docs.stoobly.com/core-concepts/agent/proxy-settings
4
+
5
+ echo "Configuring rewrite rules..."
6
+ stoobly-agent config rewrite set \
7
+ --method GET --method POST --method OPTIONS --method PUT --method DELETE \
8
+ --mode record \
9
+ --name authorization \
10
+ --pattern ".*" \
11
+ --type Header \
12
+ --value '' \
13
+
14
+ stoobly-agent config rewrite set \
15
+ --method GET --method POST --method OPTIONS --method PUT --method DELETE \
16
+ --mode record \
17
+ --name cookie \
18
+ --pattern ".*" \
19
+ --type Header \
20
+ --value '' \
21
+
22
+ stoobly-agent config rewrite set \
23
+ --method GET --method POST --method OPTIONS --method PUT --method DELETE \
24
+ --mode record \
25
+ --name set-cookie \
26
+ --pattern ".*" \
27
+ --type 'Response Header' \
28
+ --value '' \