stoobly-agent 1.10.4__py3-none-any.whl → 1.10.6__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/app/cli/scaffold/docker/workflow/run_command.py +47 -34
- stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +46 -22
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +30 -20
- stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.down +0 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.logs +1 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up +0 -1
- stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run +2 -2
- stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run +2 -2
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +5 -0
- stoobly_agent/app/cli/scaffold_cli.py +98 -59
- stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py +2 -1
- stoobly_agent/test/app/cli/scaffold/local/cli_invoker.py +2 -1
- {stoobly_agent-1.10.4.dist-info → stoobly_agent-1.10.6.dist-info}/METADATA +1 -1
- {stoobly_agent-1.10.4.dist-info → stoobly_agent-1.10.6.dist-info}/RECORD +18 -18
- {stoobly_agent-1.10.4.dist-info → stoobly_agent-1.10.6.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.10.4.dist-info → stoobly_agent-1.10.6.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-1.10.4.dist-info → stoobly_agent-1.10.6.dist-info}/licenses/LICENSE +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.10.
|
2
|
+
VERSION = '1.10.6'
|
@@ -57,29 +57,17 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
57
57
|
def timestamp_file_name(self, workflow_name: str):
|
58
58
|
return f"{workflow_name}{self.timestamp_file_extension}"
|
59
59
|
|
60
|
-
def __create_timestamp_file(self):
|
61
|
-
# Create timestamp file to indicate workflow is starting
|
62
|
-
timestamp_file = self.timestamp_file_path
|
63
|
-
|
64
|
-
try:
|
65
|
-
with open(timestamp_file, 'w') as f:
|
66
|
-
f.write(str(time.time()))
|
67
|
-
Logger.instance(LOG_ID).debug(f"Created timestamp file: {timestamp_file}")
|
68
|
-
except Exception as e:
|
69
|
-
Logger.instance(LOG_ID).error(f"Failed to create timestamp file: {e}")
|
70
|
-
sys.exit(1)
|
71
|
-
|
72
|
-
return timestamp_file
|
73
|
-
|
74
60
|
def up(self, **options: WorkflowUpOptions):
|
75
61
|
"""Execute the complete Docker workflow up process."""
|
76
62
|
|
77
63
|
no_publish = options.get('no_publish', False)
|
78
64
|
print_service_header = options.get('print_service_header')
|
65
|
+
timestamp_file = None
|
79
66
|
|
80
|
-
self.
|
67
|
+
if not self.dry_run:
|
68
|
+
self.__iterate_active_workflows(handle_active=self.__handle_up_active)
|
81
69
|
|
82
|
-
|
70
|
+
timestamp_file = self.__create_timestamp_file()
|
83
71
|
|
84
72
|
try:
|
85
73
|
# Create individual service commands
|
@@ -132,18 +120,17 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
132
120
|
self.exec(exec_command)
|
133
121
|
|
134
122
|
except Exception as e:
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
os.remove(timestamp_file)
|
139
|
-
Logger.instance(LOG_ID).info(f"Removed timestamp file due to error: {timestamp_file}")
|
140
|
-
except Exception as cleanup_error:
|
141
|
-
Logger.instance(LOG_ID).warning(f"Failed to remove timestamp file after error: {cleanup_error}")
|
123
|
+
if timestamp_file:
|
124
|
+
# Clean up timestamp file on error
|
125
|
+
self.__remove_timestamp_file(timestamp_file)
|
142
126
|
raise e
|
143
127
|
|
144
128
|
def down(self, **options: WorkflowDownOptions):
|
145
129
|
"""Execute the complete Docker workflow down process."""
|
146
|
-
|
130
|
+
|
131
|
+
timestamp_file = None
|
132
|
+
if not self.dry_run:
|
133
|
+
timestamp_file = self.__find_and_verify_timestamp_file()
|
147
134
|
|
148
135
|
print_service_header = options.get('print_service_header')
|
149
136
|
|
@@ -197,17 +184,15 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
197
184
|
remove_ingress_network_command = command.remove_ingress_network()
|
198
185
|
if remove_ingress_network_command:
|
199
186
|
self.exec(remove_ingress_network_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
try:
|
204
|
-
os.remove(timestamp_file)
|
205
|
-
except Exception as e:
|
206
|
-
Logger.instance(LOG_ID).warning(f"Failed to remove timestamp file: {e}")
|
187
|
+
|
188
|
+
if timestamp_file:
|
189
|
+
self.__remove_timestamp_file(timestamp_file)
|
207
190
|
|
208
191
|
def logs(self, **options: WorkflowLogsOptions):
|
209
192
|
"""Execute the complete Docker workflow logs process."""
|
210
|
-
|
193
|
+
|
194
|
+
if not self.dry_run:
|
195
|
+
self.__find_and_verify_timestamp_file()
|
211
196
|
|
212
197
|
print_service_header = options.get('print_service_header')
|
213
198
|
|
@@ -418,7 +403,7 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
418
403
|
if self.script:
|
419
404
|
print(command, file=self.script)
|
420
405
|
|
421
|
-
if self.dry_run:
|
406
|
+
if self.dry_run or self.containerized:
|
422
407
|
print(command)
|
423
408
|
else:
|
424
409
|
result = subprocess.run(command, shell=True, **options)
|
@@ -430,6 +415,7 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
430
415
|
# Check if workflow is running (timestamp file exists)
|
431
416
|
|
432
417
|
timestamp_file = self.timestamp_file_path
|
418
|
+
|
433
419
|
if not os.path.exists(timestamp_file):
|
434
420
|
Logger.instance(LOG_ID).error(f"Workflow '{self.workflow_name}' is not running.")
|
435
421
|
|
@@ -449,6 +435,11 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
449
435
|
if folder != self.workflow_name:
|
450
436
|
workflow_name = file_name.split(self.timestamp_file_extension)[0]
|
451
437
|
|
438
|
+
# If the workflow is namespaced, allow it to run at the same time
|
439
|
+
# Same workflow with same namespace is allowed, workflow will be restarted
|
440
|
+
if workflow_name == self.workflow_name:
|
441
|
+
return
|
442
|
+
|
452
443
|
Logger.instance(LOG_ID).error(f"Workflow '{workflow_name}' is running, please stop it first.")
|
453
444
|
|
454
445
|
if folder != workflow_name:
|
@@ -483,4 +474,26 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
483
474
|
continue
|
484
475
|
|
485
476
|
if handle_active:
|
486
|
-
handle_active(folder, timestamp_file_path)
|
477
|
+
handle_active(folder, timestamp_file_path)
|
478
|
+
|
479
|
+
def __create_timestamp_file(self):
|
480
|
+
# Create timestamp file to indicate workflow is starting
|
481
|
+
timestamp_file = self.timestamp_file_path
|
482
|
+
|
483
|
+
try:
|
484
|
+
with open(timestamp_file, 'w') as f:
|
485
|
+
f.write(str(time.time()))
|
486
|
+
Logger.instance(LOG_ID).debug(f"Created timestamp file: {timestamp_file}")
|
487
|
+
except Exception as e:
|
488
|
+
Logger.instance(LOG_ID).error(f"Failed to create timestamp file: {e}")
|
489
|
+
sys.exit(1)
|
490
|
+
|
491
|
+
return timestamp_file
|
492
|
+
|
493
|
+
def __remove_timestamp_file(self, timestamp_file: str):
|
494
|
+
# Clean up timestamp file
|
495
|
+
if os.path.exists(timestamp_file):
|
496
|
+
try:
|
497
|
+
os.remove(timestamp_file)
|
498
|
+
except Exception as e:
|
499
|
+
Logger.instance(LOG_ID).warning(f"Failed to remove timestamp file: {e}")
|
@@ -3,6 +3,7 @@ import pdb
|
|
3
3
|
import signal
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
|
+
import time
|
6
7
|
|
7
8
|
from types import FunctionType
|
8
9
|
from typing import Optional, List
|
@@ -49,12 +50,6 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
49
50
|
def pid_file_name(self, workflow_name: str):
|
50
51
|
return f"{workflow_name}{self.pid_file_extension}"
|
51
52
|
|
52
|
-
def _write_pid(self, pid: int):
|
53
|
-
"""Write the process PID to the PID file."""
|
54
|
-
os.makedirs(os.path.dirname(self.pid_file_path), exist_ok=True)
|
55
|
-
with open(self.pid_file_path, 'w') as f:
|
56
|
-
f.write(str(pid))
|
57
|
-
|
58
53
|
def _read_pid(self, file_path = None) -> Optional[int]:
|
59
54
|
"""Read the process PID from the PID file."""
|
60
55
|
file_path = file_path or self.pid_file_path
|
@@ -139,7 +134,8 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
139
134
|
"""Start the workflow using local stoobly-agent run."""
|
140
135
|
detached = options.get('detached', False)
|
141
136
|
|
142
|
-
|
137
|
+
if not self.dry_run:
|
138
|
+
self.__iterate_active_workflows(handle_active=self.__handle_up_active, handle_stale=self.__handle_up_stale)
|
143
139
|
|
144
140
|
# iterate through each service in the workflow
|
145
141
|
commands = self.workflow_service_commands(**options)
|
@@ -167,6 +163,7 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
167
163
|
def down(self, **options: WorkflowDownOptions):
|
168
164
|
"""Stop the workflow by killing the local process."""
|
169
165
|
|
166
|
+
# Intentially run this during dry run, we need the PID to be returned
|
170
167
|
pid = self.__find_and_verify_workflow_pid()
|
171
168
|
if not pid:
|
172
169
|
return
|
@@ -204,8 +201,7 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
204
201
|
Logger.instance(LOG_ID).info(f"Successfully stopped process {pid} for {self.workflow_name}")
|
205
202
|
|
206
203
|
# Clean up PID file
|
207
|
-
|
208
|
-
os.remove(self.pid_file_path)
|
204
|
+
self.__remove_pid_file()
|
209
205
|
|
210
206
|
except Exception as e:
|
211
207
|
Logger.instance(LOG_ID).error(f"Failed to stop {self.workflow_name}: {e}")
|
@@ -213,9 +209,9 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
213
209
|
def logs(self, **options: WorkflowLogsOptions):
|
214
210
|
"""Show logs for the local workflow process."""
|
215
211
|
follow = options.get('follow', False)
|
216
|
-
|
217
|
-
|
218
|
-
|
212
|
+
|
213
|
+
if not self.dry_run:
|
214
|
+
self.__find_and_verify_workflow_pid()
|
219
215
|
|
220
216
|
# Build log command
|
221
217
|
log_file = f"{self.log_file_path}"
|
@@ -238,6 +234,12 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
238
234
|
commands.sort(key=lambda command: command.service_config.priority)
|
239
235
|
return commands
|
240
236
|
|
237
|
+
def __create_pid_file(self, pid: int):
|
238
|
+
"""Write the process PID to the PID file."""
|
239
|
+
os.makedirs(os.path.dirname(self.pid_file_path), exist_ok=True)
|
240
|
+
with open(self.pid_file_path, 'w') as f:
|
241
|
+
f.write(str(pid))
|
242
|
+
|
241
243
|
def __dry_run_down(self, pid: int, output_file: str):
|
242
244
|
print(f"# Stop {self.workflow_name} (PID: {pid})", file=output_file)
|
243
245
|
print(f"kill {pid} || true", file=output_file)
|
@@ -253,7 +255,9 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
253
255
|
print(f"cat {log_file}", file=output_file)
|
254
256
|
|
255
257
|
def __find_and_verify_workflow_pid(self):
|
258
|
+
# Find and verify the workflow PID
|
256
259
|
pid = self._read_pid()
|
260
|
+
|
257
261
|
if not pid:
|
258
262
|
Logger.instance(LOG_ID).error(f"Workflow {self.workflow_name} is not running.")
|
259
263
|
|
@@ -268,9 +272,7 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
268
272
|
if not self._is_process_running(pid):
|
269
273
|
Logger.instance(LOG_ID).info(f"Process {pid} for {self.workflow_name} is not running")
|
270
274
|
# Clean up PID file
|
271
|
-
|
272
|
-
os.remove(self.pid_file_path)
|
273
|
-
return
|
275
|
+
return self.__remove_pid_file()
|
274
276
|
|
275
277
|
return pid
|
276
278
|
|
@@ -281,9 +283,16 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
281
283
|
else:
|
282
284
|
file_name = os.path.basename(pid_file_path)
|
283
285
|
workflow_name = self.workflow_name
|
286
|
+
|
287
|
+
# If the folder is not the same as the workflow name, then the workflow is namespaced, get the real workflow name
|
284
288
|
if folder != self.workflow_name:
|
285
289
|
workflow_name = file_name.split(self.pid_file_extension)[0]
|
286
290
|
|
291
|
+
# If the workflow is namespaced, allow it to run at the same time
|
292
|
+
# Same workflow with same namespace is covered by pid_file_path check
|
293
|
+
if workflow_name == self.workflow_name:
|
294
|
+
return
|
295
|
+
|
287
296
|
Logger.instance(LOG_ID).error(f"Workflow {workflow_name} is already running with PID {pid}")
|
288
297
|
|
289
298
|
if folder != workflow_name:
|
@@ -325,6 +334,10 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
325
334
|
if handle_stale:
|
326
335
|
handle_stale(folder, pid, pid_file_path)
|
327
336
|
|
337
|
+
def __remove_pid_file(self):
|
338
|
+
if os.path.exists(self.pid_file_path):
|
339
|
+
os.remove(self.pid_file_path)
|
340
|
+
|
328
341
|
def __up_command(self, public_directory_paths: List[str], response_fixtures_paths: List[str], **options: WorkflowUpOptions):
|
329
342
|
# Build the stoobly-agent run command
|
330
343
|
command = ['stoobly-agent', 'run']
|
@@ -359,7 +372,7 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
359
372
|
for line in script_lines:
|
360
373
|
print(line, file=self.script)
|
361
374
|
|
362
|
-
if self.dry_run:
|
375
|
+
if self.dry_run or self.containerized:
|
363
376
|
print(command_str)
|
364
377
|
else:
|
365
378
|
# Execute directly
|
@@ -372,20 +385,31 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
372
385
|
check=True
|
373
386
|
)
|
374
387
|
|
388
|
+
time.sleep(1) # Wait for the process to start
|
389
|
+
|
375
390
|
if result.returncode != 0:
|
376
|
-
|
377
|
-
sys.exit(1)
|
391
|
+
self.__handle_up_error()
|
378
392
|
|
379
393
|
# The --detached option prints the PID to stdout
|
380
394
|
pid = int(result.stdout.strip())
|
395
|
+
|
396
|
+
if not self._is_process_running(pid):
|
397
|
+
self.__handle_up_error()
|
381
398
|
|
382
399
|
# Write PID to file
|
383
|
-
self.
|
400
|
+
self.__create_pid_file(pid)
|
384
401
|
|
385
402
|
Logger.instance(LOG_ID).info(f"Started {self.workflow_name} with PID: {pid}")
|
386
403
|
except subprocess.CalledProcessError as e:
|
387
|
-
|
388
|
-
return None
|
404
|
+
self.__handle_up_error()
|
389
405
|
except ValueError as e:
|
390
406
|
Logger.instance(LOG_ID).error(f"Failed to parse PID from output: {e}")
|
391
|
-
return None
|
407
|
+
return None
|
408
|
+
|
409
|
+
def __handle_up_error(self):
|
410
|
+
log_file = f"{self.log_file_path}"
|
411
|
+
# Read log file it exists and print to stderr
|
412
|
+
if os.path.exists(log_file):
|
413
|
+
with open(log_file, 'r') as f:
|
414
|
+
print(f.read(), file=sys.stderr)
|
415
|
+
sys.exit(1)
|
@@ -2,9 +2,11 @@
|
|
2
2
|
#
|
3
3
|
# STOOBLY_APP_DIR: path to the application source code directory, defaults to $(pwd)
|
4
4
|
# STOOBLY_CA_CERTS_DIR: path to folder where ca certs are stored, defaults to $(pwd)/.stoobly/ca_certs
|
5
|
+
# STOOBLY_CA_CERTS_INSTALL_CONFIRM: confirm answer to CA certificate installation prompt
|
5
6
|
# STOOBLY_CERTS_DIR: path to a folder to store certs, defaults to $(pwd)/.stoobly/certs
|
6
7
|
# STOOBLY_CONTEXT_DIR: path to the folder containing the .stoobly folder, defaults to $(pwd)
|
7
8
|
# STOOBLY_DOTENV_FILE: path to dotenv file, defaults to $(pwd)/.env
|
9
|
+
# STOOBLY_HOSTNAME_INSTALL_CONFIRM: confirm answer to hostname installation prompt
|
8
10
|
# STOOBLY_WORKFLOW_SERVICE_OPTIONS: extra --service options to pass 'stoobly-agent scaffold workflow' commands
|
9
11
|
|
10
12
|
# Overridable Options
|
@@ -68,16 +70,17 @@ stoobly_exec_env=$(exec_env) CONTEXT_DIR="$(context_dir)"
|
|
68
70
|
stoobly_exec_run=$(stoobly_exec_build) && $(stoobly_exec_run_env) $(exec_up)
|
69
71
|
stoobly_exec_run_env=$(exec_env) CONTEXT_DIR="$(app_dir)"
|
70
72
|
|
71
|
-
# Workflow run
|
72
|
-
workflow_run=bash "$(app_dir)/$(workflow_script)"
|
73
|
-
|
74
73
|
action/install:
|
75
74
|
$(eval action=install)
|
76
75
|
action/uninstall:
|
77
76
|
$(eval action=uninstall)
|
78
77
|
ca-cert/install: stoobly/install
|
79
78
|
@if [ -z "$$(ls $(ca_certs_dir) 2> /dev/null)" ]; then \
|
80
|
-
|
79
|
+
if [ -n "$$STOOBLY_CA_CERTS_INSTALL_CONFIRM" ]; then \
|
80
|
+
confirm="$$STOOBLY_CA_CERTS_INSTALL_CONFIRM"; \
|
81
|
+
else \
|
82
|
+
read -p "Installing CA certificate is required for $(workflow)ing requests, continue? (y/N) " confirm; \
|
83
|
+
fi && \
|
81
84
|
if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \
|
82
85
|
echo "Running stoobly-agent ca-cert install..."; \
|
83
86
|
stoobly-agent ca-cert install --ca-certs-dir-path $(ca_certs_dir); \
|
@@ -107,10 +110,10 @@ intercept/disable:
|
|
107
110
|
intercept/enable:
|
108
111
|
@export EXEC_COMMAND=intercept/.enable EXEC_OPTIONS="" EXEC_ARGS=$(scenario_key) && \
|
109
112
|
$(stoobly_exec)
|
110
|
-
mock: workflow/mock ca-cert/install workflow/hostname/install
|
113
|
+
mock: workflow/mock ca-cert/install workflow/up nameservers workflow/hostname/install workflow/up/run
|
111
114
|
mock/services: workflow/mock workflow/services
|
112
|
-
mock/logs: workflow/mock workflow/logs
|
113
|
-
mock/down: workflow/mock workflow/down workflow/hostname/uninstall
|
115
|
+
mock/logs: workflow/mock workflow/logs workflow/logs/run
|
116
|
+
mock/down: workflow/mock workflow/down workflow/down/run workflow/hostname/uninstall
|
114
117
|
pipx/install:
|
115
118
|
@if ! command -v pipx >/dev/null 2>&1; then \
|
116
119
|
echo "pipx is not installed. Installing pipx..."; \
|
@@ -121,10 +124,10 @@ python/validate:
|
|
121
124
|
echo "Error: Python 3.10, 3.11, or 3.12 is required."; \
|
122
125
|
exit 1; \
|
123
126
|
fi
|
124
|
-
record: workflow/record ca-cert/install workflow/hostname/install
|
125
|
-
record/down: workflow/record workflow/down workflow/hostname/uninstall
|
127
|
+
record: workflow/record ca-cert/install workflow/up nameservers workflow/hostname/install workflow/up/run
|
128
|
+
record/down: workflow/record workflow/down workflow/down/run workflow/hostname/uninstall
|
126
129
|
record/services: workflow/record workflow/services
|
127
|
-
record/logs: workflow/record workflow/logs
|
130
|
+
record/logs: workflow/record workflow/logs workflow/logs/run
|
128
131
|
scenario/create:
|
129
132
|
# Create a scenario
|
130
133
|
@export EXEC_COMMAND=scenario/.create EXEC_OPTIONS="$(options)" EXEC_ARGS="$(name)" && \
|
@@ -154,18 +157,23 @@ stoobly/install: python/validate pipx/install
|
|
154
157
|
echo "stoobly-agent not found. Installing..."; \
|
155
158
|
pipx install stoobly-agent || { echo "Failed to install stoobly-agent"; exit 1; }; \
|
156
159
|
fi
|
157
|
-
test: workflow/test workflow/up
|
160
|
+
test: workflow/test workflow/up workflow/up/run
|
158
161
|
test/services: workflow/test workflow/services
|
159
|
-
test/logs: workflow/test workflow/logs
|
160
|
-
test/down: workflow/test workflow/down
|
162
|
+
test/logs: workflow/test workflow/logs workflow/logs/run
|
163
|
+
test/down: workflow/test workflow/down workflow/down/run
|
161
164
|
tmpdir:
|
162
165
|
@mkdir -p $(app_tmp_dir)
|
163
166
|
workflow/down: dotenv
|
164
167
|
@export EXEC_COMMAND=scaffold/.down EXEC_OPTIONS="$(workflow_down_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
|
165
|
-
$(stoobly_exec_run)
|
166
|
-
|
168
|
+
$(stoobly_exec_run)
|
169
|
+
workflow/down/run:
|
170
|
+
@bash "$(app_dir)/$(workflow_script)"
|
167
171
|
workflow/hostname: stoobly/install
|
168
|
-
@
|
172
|
+
@if [ -n "$$STOOBLY_HOSTNAME_INSTALL_CONFIRM" ]; then \
|
173
|
+
confirm="$$STOOBLY_HOSTNAME_INSTALL_CONFIRM"; \
|
174
|
+
else \
|
175
|
+
read -p "Do you want to $(action) hostname(s) in /etc/hosts? (y/N) " confirm; \
|
176
|
+
fi && \
|
169
177
|
if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \
|
170
178
|
CURRENT_VERSION=$$(stoobly-agent --version); \
|
171
179
|
REQUIRED_VERSION="1.4.0"; \
|
@@ -180,8 +188,9 @@ workflow/hostname/install: action/install workflow/hostname
|
|
180
188
|
workflow/hostname/uninstall: action/uninstall workflow/hostname
|
181
189
|
workflow/logs:
|
182
190
|
@export EXEC_COMMAND=scaffold/.logs EXEC_OPTIONS="$(workflow_log_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
|
183
|
-
$(stoobly_exec_run)
|
184
|
-
|
191
|
+
$(stoobly_exec_run)
|
192
|
+
workflow/logs/run:
|
193
|
+
@bash "$(app_dir)/$(workflow_script)"
|
185
194
|
workflow/mock:
|
186
195
|
$(eval workflow=mock)
|
187
196
|
workflow/namespace: tmpdir
|
@@ -195,5 +204,6 @@ workflow/test:
|
|
195
204
|
$(eval workflow=test) $(eval workflow_up_extra_options=$(workflow_up_extra_options) --no-publish)
|
196
205
|
workflow/up: dotenv
|
197
206
|
@export EXEC_COMMAND=scaffold/.up EXEC_OPTIONS="$(workflow_up_options) $(workflow_run_options) $(options)" EXEC_ARGS="$(workflow)" && \
|
198
|
-
$(stoobly_exec_run)
|
199
|
-
|
207
|
+
$(stoobly_exec_run)
|
208
|
+
workflow/up/run:
|
209
|
+
@bash "$(app_dir)/$(workflow_script)"
|
@@ -3,6 +3,8 @@
|
|
3
3
|
# This file was automatically generated. DO NOT EDIT.
|
4
4
|
# Any changes made to this file will be overwritten.
|
5
5
|
|
6
|
+
npx cypress run --project .
|
7
|
+
|
6
8
|
if [ -f .env ]; then
|
7
9
|
set -a; . ./.env; set +a;
|
8
10
|
fi
|
@@ -12,8 +14,6 @@ export http_proxy=http://localhost:$APP_PROXY_PORT
|
|
12
14
|
export HTTPS_PROXY=http://localhost:$APP_PROXY_PORT
|
13
15
|
export https_proxy=http://localhost:$APP_PROXY_PORT
|
14
16
|
|
15
|
-
npx cypress run --project .
|
16
|
-
|
17
17
|
entrypoint=$1
|
18
18
|
|
19
19
|
if [ -e "$entrypoint" ]; then
|
@@ -3,6 +3,8 @@
|
|
3
3
|
# This file was automatically generated. DO NOT EDIT.
|
4
4
|
# Any changes made to this file will be overwritten.
|
5
5
|
|
6
|
+
npx playwright test --reporter dot
|
7
|
+
|
6
8
|
if [ -f .env ]; then
|
7
9
|
set -a; . ./.env; set +a;
|
8
10
|
fi
|
@@ -12,8 +14,6 @@ export http_proxy=http://localhost:$APP_PROXY_PORT
|
|
12
14
|
export HTTPS_PROXY=http://localhost:$APP_PROXY_PORT
|
13
15
|
export https_proxy=http://localhost:$APP_PROXY_PORT
|
14
16
|
|
15
|
-
npx playwright test --reporter dot
|
16
|
-
|
17
17
|
entrypoint=$1
|
18
18
|
|
19
19
|
if [ -e "$entrypoint" ]; then
|
@@ -28,6 +28,7 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
28
28
|
self.__current_working_dir = os.getcwd()
|
29
29
|
self.__ca_certs_dir_path = kwargs.get('ca_certs_dir_path') or app.ca_certs_dir_path
|
30
30
|
self.__certs_dir_path = kwargs.get('certs_dir_path') or app.certs_dir_path
|
31
|
+
self.__containerized = kwargs.get('containerized') or False
|
31
32
|
self.__context_dir_path = kwargs.get('context_dir_path') or app.context_dir_path
|
32
33
|
self.__dry_run = kwargs.get('dry_run', False)
|
33
34
|
self.__namespace = kwargs.get('namespace') or self.workflow_name
|
@@ -48,6 +49,10 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
48
49
|
def certs_dir_path(self):
|
49
50
|
return self.__certs_dir_path
|
50
51
|
|
52
|
+
@property
|
53
|
+
def containerized(self):
|
54
|
+
return self.__containerized
|
55
|
+
|
51
56
|
@property
|
52
57
|
def context_dir_path(self):
|
53
58
|
if not self.__context_dir_path:
|
@@ -341,6 +341,7 @@ def copy(**kwargs):
|
|
341
341
|
@click.option('--context-dir-path', default=data_dir.context_dir_path, help='Path to Stoobly data directory.')
|
342
342
|
@click.option('--containerized', is_flag=True, help='Set if run from within a container.')
|
343
343
|
@click.option('--dry-run', default=False, is_flag=True)
|
344
|
+
@click.option('--hostname-uninstall-confirm', default=None, type=click.Choice(['y', 'Y', 'n', 'N']), help='Confirm answer to hostname uninstall prompt.')
|
344
345
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
345
346
|
Log levels can be "debug", "info", "warning", or "error"
|
346
347
|
''')
|
@@ -385,7 +386,20 @@ def down(**kwargs):
|
|
385
386
|
script=script,
|
386
387
|
**kwargs
|
387
388
|
)
|
388
|
-
|
389
|
+
|
390
|
+
# Because test workflow is completely containerized, we don't need to prompt to install hostnames in /etc/hosts
|
391
|
+
# Entrypoint container will be within the container network
|
392
|
+
if workflow_command.workflow_template != WORKFLOW_TEST_TYPE:
|
393
|
+
if not containerized and not kwargs['dry_run']:
|
394
|
+
# Prompt confirm to install hostnames
|
395
|
+
if kwargs.get('hostname_uninstall_confirm'):
|
396
|
+
confirm = kwargs['hostname_uninstall_confirm']
|
397
|
+
else:
|
398
|
+
confirm = input(f"Do you want to uninstall hostnames for {kwargs['workflow_name']}? (y/N) ")
|
399
|
+
|
400
|
+
if confirm == "y" or confirm == "Y":
|
401
|
+
__hostname_uninstall(app_dir_path=kwargs['app_dir_path'], service=kwargs['service'], workflow=[kwargs['workflow_name']])
|
402
|
+
|
389
403
|
# Execute the workflow down
|
390
404
|
workflow_command.down(
|
391
405
|
**command_args,
|
@@ -401,6 +415,7 @@ def down(**kwargs):
|
|
401
415
|
@click.option(
|
402
416
|
'--container', multiple=True, help=f"Select which containers to log. Defaults to '{WORKFLOW_CONTAINER_PROXY}'"
|
403
417
|
)
|
418
|
+
@click.option('--containerized', is_flag=True, help='Set if run from within a container.')
|
404
419
|
@click.option('--dry-run', default=False, is_flag=True, help='If set, prints commands.')
|
405
420
|
@click.option('--follow', is_flag=True, help='Follow last container log output.')
|
406
421
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
@@ -457,11 +472,13 @@ def logs(**kwargs):
|
|
457
472
|
@workflow.command()
|
458
473
|
@click.option('--app-dir-path', default=current_working_dir, help='Path to application directory.')
|
459
474
|
@click.option('--ca-certs-dir-path', default=data_dir.ca_certs_dir_path, help='Path to ca certs directory used to sign SSL certs.')
|
475
|
+
@click.option('--ca-certs-install-confirm', default=None, type=click.Choice(['y', 'Y', 'n', 'N']), help='Confirm answer to CA certificate installation prompt.')
|
460
476
|
@click.option('--certs-dir-path', help='Path to certs directory. Defaults to the certs dir of the context.')
|
461
477
|
@click.option('--containerized', is_flag=True, help='Set if run from within a container.')
|
462
478
|
@click.option('--context-dir-path', default=data_dir.context_dir_path, help='Path to Stoobly data directory.')
|
463
479
|
@click.option('--detached', is_flag=True, help='If set, will run the highest priority service in the background.')
|
464
480
|
@click.option('--dry-run', default=False, is_flag=True, help='If set, prints commands.')
|
481
|
+
@click.option('--hostname-install-confirm', default=None, type=click.Choice(['y', 'Y', 'n', 'N']), help='Confirm answer to hostname installation prompt.')
|
465
482
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
466
483
|
Log levels can be "debug", "info", "warning", or "error"
|
467
484
|
''')
|
@@ -472,7 +489,6 @@ def logs(**kwargs):
|
|
472
489
|
@click.option('--service', multiple=True, help='Select which services to run. Defaults to all.')
|
473
490
|
@click.option('--user-id', default=os.getuid(), help='OS user ID of the owner of context dir path.')
|
474
491
|
@click.option('--verbose', is_flag=True)
|
475
|
-
@click.option('-y', '--yes', is_flag=True, help='Auto-confirm CA certificate installation prompt.')
|
476
492
|
@click.argument('workflow_name')
|
477
493
|
def up(**kwargs):
|
478
494
|
os.environ[env_vars.LOG_LEVEL] = kwargs['log_level']
|
@@ -491,17 +507,15 @@ def up(**kwargs):
|
|
491
507
|
# First time if folder does not exist or is empty
|
492
508
|
first_time = not os.path.exists(app.ca_certs_dir_path) or not os.listdir(app.ca_certs_dir_path)
|
493
509
|
if first_time and not containerized and not dry_run:
|
494
|
-
|
495
|
-
|
496
|
-
# Auto-confirm if -y/--yes option is provided
|
497
|
-
ca_cert_install(app.ca_certs_dir_path)
|
510
|
+
if kwargs.get('ca_certs_install_confirm'):
|
511
|
+
confirm = kwargs['ca_certs_install_confirm']
|
498
512
|
else:
|
499
513
|
confirm = input(f"Installing CA certificate is required for {kwargs['workflow_name']}ing requests, continue? (y/N) ")
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
514
|
+
|
515
|
+
if confirm == "y" or confirm == "Y":
|
516
|
+
ca_cert_install(app.ca_certs_dir_path)
|
517
|
+
else:
|
518
|
+
print("You can install the CA certificate later by running: stoobly-agent ca-cert install")
|
505
519
|
|
506
520
|
services = __get_services(
|
507
521
|
app, service=kwargs['service'], workflow=[kwargs['workflow_name']]
|
@@ -533,6 +547,19 @@ def up(**kwargs):
|
|
533
547
|
**kwargs
|
534
548
|
)
|
535
549
|
|
550
|
+
# Because test workflow is complete containerized, we don't need to prompt to install hostnames in /etc/hosts
|
551
|
+
# Entrypoint container will be within the container network
|
552
|
+
if workflow_command.workflow_template != WORKFLOW_TEST_TYPE:
|
553
|
+
if not containerized and not dry_run:
|
554
|
+
# Prompt confirm to install hostnames
|
555
|
+
if kwargs.get('hostname_install_confirm'):
|
556
|
+
confirm = kwargs['hostname_install_confirm']
|
557
|
+
else:
|
558
|
+
confirm = input(f"Do you want to install hostnames for {kwargs['workflow_name']}? (y/N) ")
|
559
|
+
|
560
|
+
if confirm == "y" or confirm == "Y":
|
561
|
+
__hostname_install(app_dir_path=kwargs['app_dir_path'], service=kwargs['service'], workflow=[kwargs['workflow_name']])
|
562
|
+
|
536
563
|
if first_time and not containerized and not dry_run:
|
537
564
|
options = {}
|
538
565
|
|
@@ -596,30 +623,7 @@ def validate(**kwargs):
|
|
596
623
|
@click.option('--service', multiple=True, help='Select specific services. Defaults to all.')
|
597
624
|
@click.option('--workflow', multiple=True, help='Specify services by workflow(s). Defaults to all.')
|
598
625
|
def install(**kwargs):
|
599
|
-
|
600
|
-
__validate_app(app)
|
601
|
-
|
602
|
-
services = __get_services(
|
603
|
-
app, service=kwargs['service'], without_core=True, workflow=kwargs['workflow']
|
604
|
-
)
|
605
|
-
|
606
|
-
hostnames = []
|
607
|
-
for service_name in services:
|
608
|
-
service = Service(service_name, app)
|
609
|
-
__validate_service_dir(service.dir_path)
|
610
|
-
|
611
|
-
service_config = ServiceConfig(service.dir_path)
|
612
|
-
if service_config.hostname:
|
613
|
-
hostnames.append(service_config.hostname)
|
614
|
-
|
615
|
-
__elevate_sudo()
|
616
|
-
|
617
|
-
try:
|
618
|
-
hosts_file_manager = HostsFileManager()
|
619
|
-
hosts_file_manager.install_hostnames(hostnames)
|
620
|
-
except PermissionError:
|
621
|
-
print("Permission denied. Please run this command with sudo.", file=sys.stderr)
|
622
|
-
sys.exit(1)
|
626
|
+
__hostname_install(**kwargs)
|
623
627
|
|
624
628
|
@hostname.command(
|
625
629
|
help="Delete from the system hosts file all scaffold service hostnames"
|
@@ -628,30 +632,7 @@ def install(**kwargs):
|
|
628
632
|
@click.option('--service', multiple=True, help='Select specific services. Defaults to all.')
|
629
633
|
@click.option('--workflow', multiple=True, help='Specify services by workflow(s). Defaults to all.')
|
630
634
|
def uninstall(**kwargs):
|
631
|
-
|
632
|
-
__validate_app(app)
|
633
|
-
|
634
|
-
services = __get_services(
|
635
|
-
app, service=kwargs['service'], without_core=True, workflow=kwargs['workflow']
|
636
|
-
)
|
637
|
-
|
638
|
-
hostnames = []
|
639
|
-
for service_name in services:
|
640
|
-
service = Service(service_name, app)
|
641
|
-
__validate_service_dir(service.dir_path)
|
642
|
-
|
643
|
-
service_config = ServiceConfig(service.dir_path)
|
644
|
-
if service_config.hostname:
|
645
|
-
hostnames.append(service_config.hostname)
|
646
|
-
|
647
|
-
__elevate_sudo()
|
648
|
-
|
649
|
-
try:
|
650
|
-
hosts_file_manager = HostsFileManager()
|
651
|
-
hosts_file_manager.uninstall_hostnames(hostnames)
|
652
|
-
except PermissionError:
|
653
|
-
print("Permission denied. Please run this command with sudo.", file=sys.stderr)
|
654
|
-
sys.exit(1)
|
635
|
+
__hostname_uninstall(**kwargs)
|
655
636
|
|
656
637
|
scaffold.add_command(app)
|
657
638
|
scaffold.add_command(service)
|
@@ -716,6 +697,64 @@ def __get_services(app: App, **kwargs):
|
|
716
697
|
|
717
698
|
return services
|
718
699
|
|
700
|
+
def __hostname_install(**kwargs):
|
701
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
702
|
+
__validate_app(app)
|
703
|
+
|
704
|
+
services = __get_services(
|
705
|
+
app, service=kwargs['service'], without_core=True, workflow=kwargs['workflow']
|
706
|
+
)
|
707
|
+
|
708
|
+
hostnames = []
|
709
|
+
for service_name in services:
|
710
|
+
service = Service(service_name, app)
|
711
|
+
__validate_service_dir(service.dir_path)
|
712
|
+
|
713
|
+
service_config = ServiceConfig(service.dir_path)
|
714
|
+
if service_config.hostname:
|
715
|
+
hostnames.append(service_config.hostname)
|
716
|
+
|
717
|
+
if not hostnames:
|
718
|
+
return
|
719
|
+
|
720
|
+
__elevate_sudo()
|
721
|
+
|
722
|
+
try:
|
723
|
+
hosts_file_manager = HostsFileManager()
|
724
|
+
hosts_file_manager.install_hostnames(hostnames)
|
725
|
+
except PermissionError:
|
726
|
+
print("Permission denied. Please run this command with sudo.", file=sys.stderr)
|
727
|
+
sys.exit(1)
|
728
|
+
|
729
|
+
def __hostname_uninstall(**kwargs):
|
730
|
+
app = App(kwargs['app_dir_path'], SERVICES_NAMESPACE)
|
731
|
+
__validate_app(app)
|
732
|
+
|
733
|
+
services = __get_services(
|
734
|
+
app, service=kwargs['service'], without_core=True, workflow=kwargs['workflow']
|
735
|
+
)
|
736
|
+
|
737
|
+
hostnames = []
|
738
|
+
for service_name in services:
|
739
|
+
service = Service(service_name, app)
|
740
|
+
__validate_service_dir(service.dir_path)
|
741
|
+
|
742
|
+
service_config = ServiceConfig(service.dir_path)
|
743
|
+
if service_config.hostname:
|
744
|
+
hostnames.append(service_config.hostname)
|
745
|
+
|
746
|
+
if not hostnames:
|
747
|
+
return
|
748
|
+
|
749
|
+
__elevate_sudo()
|
750
|
+
|
751
|
+
try:
|
752
|
+
hosts_file_manager = HostsFileManager()
|
753
|
+
hosts_file_manager.uninstall_hostnames(hostnames)
|
754
|
+
except PermissionError:
|
755
|
+
print("Permission denied. Please run this command with sudo.", file=sys.stderr)
|
756
|
+
sys.exit(1)
|
757
|
+
|
719
758
|
def __print_header(text: str):
|
720
759
|
Logger.instance(LOG_ID).info(f"{bcolors.OKBLUE}{text}{bcolors.ENDC}")
|
721
760
|
|
@@ -117,7 +117,7 @@ class ScaffoldCliInvoker():
|
|
117
117
|
command = ['workflow', 'up',
|
118
118
|
'--app-dir-path', app_dir_path,
|
119
119
|
'--context-dir-path', app_dir_path,
|
120
|
-
'--
|
120
|
+
'--hostname-install-confirm', 'n',
|
121
121
|
target_workflow_name,
|
122
122
|
]
|
123
123
|
result = runner.invoke(scaffold, command)
|
@@ -134,6 +134,7 @@ class ScaffoldCliInvoker():
|
|
134
134
|
command = ['workflow', 'down',
|
135
135
|
'--app-dir-path', app_dir_path,
|
136
136
|
'--context-dir-path', app_dir_path,
|
137
|
+
'--hostname-uninstall-confirm', 'n',
|
137
138
|
target_workflow_name,
|
138
139
|
]
|
139
140
|
result = runner.invoke(scaffold, command)
|
@@ -92,8 +92,9 @@ class LocalScaffoldCliInvoker():
|
|
92
92
|
def cli_workflow_up(runner: CliRunner, app_dir_path: str, target_workflow_name: str):
|
93
93
|
command = ['workflow', 'up',
|
94
94
|
'--app-dir-path', app_dir_path,
|
95
|
+
'--ca-certs-install-confirm', 'y',
|
95
96
|
'--context-dir-path', app_dir_path,
|
96
|
-
'--
|
97
|
+
'--hostname-install-confirm', 'y',
|
97
98
|
target_workflow_name,
|
98
99
|
]
|
99
100
|
result = runner.invoke(scaffold, command)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=CjtLB5bT2uBXBd0Xa_QPnx4Folf55LeK5oi441a3lco,45
|
2
2
|
stoobly_agent/__main__.py,sha256=tefOkFZeCFU4l3C-Y4R_lR9Yt-FETISiXGUnbh6Os54,146
|
3
3
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
stoobly_agent/app/api/__init__.py,sha256=NIgcbX7iiWrApsCITXlmhr4SYbWS0fwb01x-F3jTFdo,666
|
@@ -95,7 +95,7 @@ stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py,sha256=DGaSlbOvA
|
|
95
95
|
stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py,sha256=xp1TmP8drOyl9Zhm5B1ci6NqPqRFDr2yxipmvSljgiE,717
|
96
96
|
stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=QO1TbAj6QthIyHvy7itV_d9NteNcjClYaan1GX-0kLc,1201
|
97
97
|
stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=tSPnscsBZusBaSt_NlB4exrZ2MnWMRAUJgw_NaEdHiw,1199
|
98
|
-
stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=
|
98
|
+
stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=LLiC7KCgo9ZlmXcdaidmcN449GuDK_8dYykCNFjfhyo,18217
|
99
99
|
stoobly_agent/app/cli/scaffold/env.py,sha256=dT33tHoQaUxfsFCYm8kfaAv-qPVrUPmNFQmLnFQhZeQ,1107
|
100
100
|
stoobly_agent/app/cli/scaffold/hosts_file_manager.py,sha256=zNX5wh6zXQ4J2BA0YYdD7_CPqDz02b_ghXsY3oTjjB4,4999
|
101
101
|
stoobly_agent/app/cli/scaffold/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -103,7 +103,7 @@ stoobly_agent/app/cli/scaffold/local/service/__init__.py,sha256=47DEQpj8HBSa-_TI
|
|
103
103
|
stoobly_agent/app/cli/scaffold/local/service/builder.py,sha256=uZNPIQWo4UcLy3bcE6Wvntle6ONPpWjS5oAq3g0Punk,1852
|
104
104
|
stoobly_agent/app/cli/scaffold/local/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
105
105
|
stoobly_agent/app/cli/scaffold/local/workflow/builder.py,sha256=O8Iwyd231-ciQWkiGB5HAiFJXMDUtXIoyl0S_Jrj3lQ,806
|
106
|
-
stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=
|
106
|
+
stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=7XhI6X-TKAGv-HuOvh6Knm7R5DDpfi8ZZ8BJnHQwGtY,15956
|
107
107
|
stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
|
108
108
|
stoobly_agent/app/cli/scaffold/service.py,sha256=74JwjTRRkk6lo-k9hre1iGztbKa9zDqjPVx3Qgpze-s,699
|
109
109
|
stoobly_agent/app/cli/scaffold/service_command.py,sha256=j-lkG5Zth_CBHa6Z9Kv3dJwxX9gylFBZMZbW691R8ZU,1480
|
@@ -117,7 +117,7 @@ stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOW
|
|
117
117
|
stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=DPYLBAwUsh4C8ho-4SwjBe32xuBSUpWlWgRF9JIbKRc,11768
|
118
118
|
stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=x8C_a0VoO_vUbosp4_6IC1U7Ge9NnUdVKDPpVMtMkeY,171
|
119
119
|
stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=9DQK-OXnRKjjKWsUSIRAio6dkR4eGxD1vizPT7Q5sp8,159
|
120
|
-
stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=
|
120
|
+
stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=6zIVTZl23rLR8jetIr76B1dXi_Zy1SUMGZok6BTaa4A,9777
|
121
121
|
stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=SABod6_InBFyOm-uulK7r27SR0sWRUiL0h69g_jvNJA,249
|
122
122
|
stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.networks.yml,sha256=I4PbJpQjFHb5IbAUWNvYM6okDEtmwtKFDQg-yog05WM,141
|
123
123
|
stoobly_agent/app/cli/scaffold/templates/app/Makefile,sha256=TEmPG7Bf0KZOnmfsgdzza3UdwcVMmM5Lj1YdLc4cgjA,79
|
@@ -182,11 +182,11 @@ stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.init,sh
|
|
182
182
|
stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.run,sha256=LQs_bJiN3LaC0eJnaF_Fnrr9ZIlxkaM8tnWMxjCovXM,446
|
183
183
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/intercept/.disable,sha256=xVf4Pk1RLvJm7Ff0rbGoWhYHPv0ME5e93fxS2yFqLnE,45
|
184
184
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/intercept/.enable,sha256=sfUSPG4uHdXX95BLgivXQYLbsLBP2DjJIiSTXRtvXuY,188
|
185
|
-
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.down,sha256=
|
186
|
-
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.logs,sha256=
|
185
|
+
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.down,sha256=Eb-DY3NYLBny2Akx4R3ckqHqi_5gzYKNp9tJgFbeFOQ,205
|
186
|
+
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.logs,sha256=ebsSW7N3RbT4asqDVpdKWzztC3FZxXLpjnxsiblMSGQ,184
|
187
187
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.mkcert,sha256=vyHaXmvy-7oL2RD8rIxwT-fdJS5kXmB0yHK5fRMo_cM,46
|
188
188
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.services,sha256=sAbaKzBxNDXimyUNinB_a2Kt47vUCa0Kd3Z1QAp2GrU,161
|
189
|
-
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up,sha256=
|
189
|
+
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up,sha256=woSsPc0S9CasNItleUD1nz6Rz-8TThamy-3_DGLMiSk,195
|
190
190
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scenario/.create,sha256=EZe84bLAKB-mrB9PGMx-amyMjp6x-ESUZKC8wxrWdLE,245
|
191
191
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scenario/.delete,sha256=RspRDQ7WT5jpN2o-6qXOlH-A2VpN4981pD4ZJljk9Rw,260
|
192
192
|
stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scenario/.list,sha256=BdX3ARC-Piuwk0vFzBrcxEwtHjdFncpORzqP-hP4IwE,84
|
@@ -206,11 +206,11 @@ stoobly_agent/app/cli/scaffold/templates/constants.py,sha256=o6SbbG0AJoIYI0cQ5qD
|
|
206
206
|
stoobly_agent/app/cli/scaffold/templates/factory.py,sha256=WuAKRGq4qMGQTxgG8Hx0Q8xzMoe87IRPfxrwcCeCP2E,2537
|
207
207
|
stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.Dockerfile.cypress,sha256=iMEONOnfsVTKpoVxv-tHDfUTCiemEMCt0LZ9QcdOyJQ,680
|
208
208
|
stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.docker-compose.yml,sha256=fPjbDnGQQj-TLGQbctHBwgXnC8zz2m9H-9uYK0I8mwk,484
|
209
|
-
stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run,sha256=
|
209
|
+
stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run,sha256=nwTqzhR5a7OgfQj7aPiq0_3_smV0qTLw1WRcPFNQEtM,475
|
210
210
|
stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.Dockerfile.playwright,sha256=LGibNX2Ar2r_yDbb4AcdCm6zzArtOHDk_cvRxszKExw,1221
|
211
211
|
stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.docker-compose.yml,sha256=1eyJ-wYLXXhiPaT9dYhORaa9IGnhSaqnMuLSwxrAPiQ,503
|
212
212
|
stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.entrypoint.sh,sha256=vIffcTuYdoOLH3GDKzoppExDhP0ls92wyPziqpxZrAc,286
|
213
|
-
stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run,sha256=
|
213
|
+
stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run,sha256=6AS9JZtoFw5eA4Gmh6wRbJq8BDujNLoifyLbRK1BkrI,482
|
214
214
|
stoobly_agent/app/cli/scaffold/templates/workflow/mock/configure,sha256=ktsd7J4r3YQbeUj7Uex7iakdcb6JAouwvlRqs-LI35c,171
|
215
215
|
stoobly_agent/app/cli/scaffold/templates/workflow/mock/docker-compose.yml,sha256=x6tofP1N9rFnlMdWKtyjzpWnX7-_h6eYihI_iDxGr90,479
|
216
216
|
stoobly_agent/app/cli/scaffold/templates/workflow/mock/fixtures.yml,sha256=CJlZ_kugygZpmyqIauBjNZxqk7XyLaa3yl3AWj8KV28,259
|
@@ -240,9 +240,9 @@ stoobly_agent/app/cli/scaffold/workflow_create_command.py,sha256=5gSkxNxrXLeobLU
|
|
240
240
|
stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=shPjoX1SWe7K6pGpZvw2fPVHWd6j_azTe58jvOjGUns,607
|
241
241
|
stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=Bke4lMOMxuDUFuAx9nlXHbKgYMO4KAg9ASHvjz4aVWc,1372
|
242
242
|
stoobly_agent/app/cli/scaffold/workflow_namesapce.py,sha256=VNaZrcqMMeqrzpPGhD9-oaZems1k0ebRc6wR74EvA8c,1170
|
243
|
-
stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=
|
243
|
+
stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=35wxoc9qH3ZGuPcKgFGQLEMvGwDVWbjpXqve4iz2wBY,6185
|
244
244
|
stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=Uo_yo6rVR1ZR7xpvsQvlH48AyMBVLRupd4G-bRjzm_Q,5584
|
245
|
-
stoobly_agent/app/cli/scaffold_cli.py,sha256=
|
245
|
+
stoobly_agent/app/cli/scaffold_cli.py,sha256=YnRKrC_XQ5OdQ1tgA-oL4rmamw70DlMm17SqIAQ9sfU,33677
|
246
246
|
stoobly_agent/app/cli/scenario_cli.py,sha256=lA9a4UNnLzrbJX5JJE07KGb5i9pBd2c2vdFUW6_7k0E,8345
|
247
247
|
stoobly_agent/app/cli/snapshot_cli.py,sha256=1Dw5JgDlmG6vctrawIRO7CdB73vAQk_wRBnPG2lVOrQ,11929
|
248
248
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
@@ -721,12 +721,12 @@ stoobly_agent/test/app/cli/request/request_reset_test.py,sha256=5My6Z452eideAOUu
|
|
721
721
|
stoobly_agent/test/app/cli/request/request_response_test.py,sha256=Fu-A8tIn016DKme4WIaPzo3YeFY-CPtTOpaSFigUVVM,1263
|
722
722
|
stoobly_agent/test/app/cli/request/request_snapshot_test.py,sha256=3kMmv0CuvnMXLgDQA-_u9S1DIiNOdL63L-IptVuOpf8,6308
|
723
723
|
stoobly_agent/test/app/cli/request/request_test_test.py,sha256=-cJNXKjgryVVfVt-7IN5fIhBwe3NjFoPmeavDH8lAjU,5527
|
724
|
-
stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py,sha256=
|
724
|
+
stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py,sha256=W4ZZgrgUU0hBm_l0x04eR_9eGVyfbLSMbDVGqhpi2qE,5391
|
725
725
|
stoobly_agent/test/app/cli/scaffold/docker/cli_test.py,sha256=wkYnRQo8OBDds-pGmdc2cYs5yxgO_2GiDxzI1imGntc,4301
|
726
726
|
stoobly_agent/test/app/cli/scaffold/docker/e2e_test.py,sha256=RxHvIMyVhpXy0fkfCJPFLZ2DtyC3dieVGETWOq-rQos,13655
|
727
727
|
stoobly_agent/test/app/cli/scaffold/hosts_file_manager_test.py,sha256=ztcPh1x0ZCW1FWA5YL4ulEVjfbW9TOPgk1bnSDPNmCw,2287
|
728
728
|
stoobly_agent/test/app/cli/scaffold/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
729
|
-
stoobly_agent/test/app/cli/scaffold/local/cli_invoker.py,sha256=
|
729
|
+
stoobly_agent/test/app/cli/scaffold/local/cli_invoker.py,sha256=YLQuz5JjvzfqFKk-BFT-Bl9EZgj37ZDZtwNsvqNRDHA,4138
|
730
730
|
stoobly_agent/test/app/cli/scaffold/local/e2e_test.py,sha256=10cFuX43dv6-L00zWHT9MV5c0idJhiLDhEw0FOOsKK8,15513
|
731
731
|
stoobly_agent/test/app/cli/scenario/scenario_create_test.py,sha256=fGqcjO1_1OvdpUMQfGRVkSyFe61u8WIcp_ndLFrf33A,3962
|
732
732
|
stoobly_agent/test/app/cli/scenario/scenario_replay_integration_test.py,sha256=NbGJzmvPsNLBR0ac65yt_cOTfpnsST1IG7i3F0euwAk,7031
|
@@ -797,8 +797,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
|
|
797
797
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
798
798
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
799
799
|
stoobly_agent/test/test_helper.py,sha256=6v4AHeqYPw7vtRoxET_ubmRWPJoSmTR_DVHay3FxNbQ,1299
|
800
|
-
stoobly_agent-1.10.
|
801
|
-
stoobly_agent-1.10.
|
802
|
-
stoobly_agent-1.10.
|
803
|
-
stoobly_agent-1.10.
|
804
|
-
stoobly_agent-1.10.
|
800
|
+
stoobly_agent-1.10.6.dist-info/METADATA,sha256=lhF6K4LrkdmPLu3ze3whqIrmF9V1yWQJO4qPrF3OAlY,3203
|
801
|
+
stoobly_agent-1.10.6.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
802
|
+
stoobly_agent-1.10.6.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
803
|
+
stoobly_agent-1.10.6.dist-info/licenses/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
|
804
|
+
stoobly_agent-1.10.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|