zrb 0.17.2__py3-none-any.whl → 0.21.0__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.
- zrb/__init__.py +2 -0
- zrb/builtin/project/add/app/generator/generator.py +1 -0
- zrb/builtin/project/add/app/generator/template/src/kebab-zrb-package-name/src/snake_zrb_package_name/snake_zrb_generator_name/_input.py +7 -4
- zrb/builtin/project/add/app/generator/template/src/kebab-zrb-package-name/src/snake_zrb_package_name/snake_zrb_generator_name/snake_zrb_generator_name.py +1 -0
- zrb/builtin/project/add/app/python/python.py +1 -0
- zrb/builtin/project/add/fastapp/app/_input.py +8 -4
- zrb/builtin/project/add/fastapp/app/app.py +1 -0
- zrb/builtin/project/add/fastapp/app/template/src/kebab-zrb-app-name/src/component/rpc/messagebus/caller.py +1 -1
- zrb/builtin/project/add/fastapp/crud/_input.py +2 -1
- zrb/builtin/project/add/fastapp/crud/crud.py +1 -0
- zrb/builtin/project/add/fastapp/crud/nodejs/codemod/package-lock.json +33 -24
- zrb/builtin/project/add/fastapp/field/field.py +1 -0
- zrb/builtin/project/add/plugin/plugin.py +5 -1
- zrb/builtin/project/add/task/cmd/add.py +1 -0
- zrb/builtin/project/add/task/docker_compose/add.py +1 -0
- zrb/builtin/project/add/task/python/add.py +1 -0
- zrb/config/config.py +1 -0
- zrb/helper/multiline.py +13 -0
- zrb/task/any_task.py +10 -3
- zrb/task/base_remote_cmd_task.py +66 -8
- zrb/task/base_task/base_task.py +10 -12
- zrb/task/base_task/component/base_task_model.py +1 -1
- zrb/task/base_task/component/common_task_model.py +35 -4
- zrb/task/checker.py +1 -1
- zrb/task/cmd_task.py +22 -9
- zrb/task/decorator.py +1 -1
- zrb/task/docker_compose_task.py +1 -1
- zrb/task/flow_task.py +45 -34
- zrb/task/http_checker.py +1 -1
- zrb/task/notifier.py +1 -1
- zrb/task/path_checker.py +1 -1
- zrb/task/path_watcher.py +1 -1
- zrb/task/port_checker.py +1 -1
- zrb/task/recurring_task.py +2 -2
- zrb/task/remote_cmd_task.py +1 -1
- zrb/task/resource_maker.py +1 -1
- zrb/task/rsync_task.py +5 -5
- zrb/task/server.py +25 -18
- zrb/task/task.py +35 -1
- zrb/task/time_watcher.py +1 -1
- zrb/task/watcher.py +5 -2
- zrb/task_env/env.py +4 -3
- zrb/task_env/env_file.py +3 -2
- zrb/task_group/group.py +1 -1
- zrb/task_input/any_input.py +5 -3
- zrb/task_input/base_input.py +54 -9
- zrb/task_input/bool_input.py +9 -6
- zrb/task_input/choice_input.py +8 -5
- zrb/task_input/float_input.py +8 -5
- zrb/task_input/int_input.py +8 -5
- zrb/task_input/multiline_input.py +118 -0
- zrb/task_input/password_input.py +9 -6
- zrb/task_input/str_input.py +18 -15
- zrb/task_input/task_input.py +4 -3
- {zrb-0.17.2.dist-info → zrb-0.21.0.dist-info}/METADATA +14 -11
- {zrb-0.17.2.dist-info → zrb-0.21.0.dist-info}/RECORD +59 -57
- {zrb-0.17.2.dist-info → zrb-0.21.0.dist-info}/LICENSE +0 -0
- {zrb-0.17.2.dist-info → zrb-0.21.0.dist-info}/WHEEL +0 -0
- {zrb-0.17.2.dist-info → zrb-0.21.0.dist-info}/entry_points.txt +0 -0
@@ -49,7 +49,7 @@ class CommonTaskModel:
|
|
49
49
|
upstreams: Iterable[AnyTask] = [],
|
50
50
|
fallbacks: Iterable[AnyTask] = [],
|
51
51
|
checkers: Iterable[AnyTask] = [],
|
52
|
-
checking_interval: Union[float, int] = 0,
|
52
|
+
checking_interval: Union[float, int] = 0.05,
|
53
53
|
run: Optional[Callable[..., Any]] = None,
|
54
54
|
on_triggered: Optional[OnTriggered] = None,
|
55
55
|
on_waiting: Optional[OnWaiting] = None,
|
@@ -65,6 +65,11 @@ class CommonTaskModel:
|
|
65
65
|
self._group = group
|
66
66
|
if group is not None:
|
67
67
|
group._add_task(self)
|
68
|
+
checkers_cp: List[AnyTask] = [checker.copy() for checker in checkers]
|
69
|
+
for checker in checkers_cp:
|
70
|
+
checker.add_env(*envs)
|
71
|
+
checker.add_env_file(*env_files)
|
72
|
+
checker.add_input(*inputs)
|
68
73
|
self._description = coalesce_str(description, name)
|
69
74
|
self._inputs = inputs
|
70
75
|
self._envs = envs
|
@@ -75,7 +80,7 @@ class CommonTaskModel:
|
|
75
80
|
self._retry_interval = retry_interval
|
76
81
|
self._upstreams = upstreams
|
77
82
|
self._fallbacks = fallbacks
|
78
|
-
self._checkers =
|
83
|
+
self._checkers = checkers_cp
|
79
84
|
self._checking_interval = checking_interval
|
80
85
|
self._run_function: Optional[Callable[..., Any]] = run
|
81
86
|
self._on_triggered = on_triggered
|
@@ -101,6 +106,9 @@ class CommonTaskModel:
|
|
101
106
|
self.__has_already_inject_fallbacks: bool = False
|
102
107
|
self.__all_inputs: Optional[List[AnyInput]] = None
|
103
108
|
|
109
|
+
def _lock_checkers(self):
|
110
|
+
self.__allow_add_checkers = False
|
111
|
+
|
104
112
|
def _lock_upstreams(self):
|
105
113
|
self.__allow_add_upstreams = False
|
106
114
|
|
@@ -164,11 +172,15 @@ class CommonTaskModel:
|
|
164
172
|
if not self.__allow_add_inputs:
|
165
173
|
raise Exception(f"Cannot insert inputs for `{self.get_name()}`")
|
166
174
|
self._inputs = list(inputs) + list(self._inputs)
|
175
|
+
for checker in self._get_checkers():
|
176
|
+
checker.insert_input(*inputs)
|
167
177
|
|
168
178
|
def add_input(self, *inputs: AnyInput):
|
169
179
|
if not self.__allow_add_inputs:
|
170
180
|
raise Exception(f"Cannot add inputs for `{self.get_name()}`")
|
171
181
|
self._inputs = list(self._inputs) + list(inputs)
|
182
|
+
for checker in self._get_checkers():
|
183
|
+
checker.add_input(*inputs)
|
172
184
|
|
173
185
|
def inject_inputs(self):
|
174
186
|
pass
|
@@ -219,11 +231,15 @@ class CommonTaskModel:
|
|
219
231
|
if not self.__allow_add_envs:
|
220
232
|
raise Exception(f"Cannot insert envs to `{self.get_name()}`")
|
221
233
|
self._envs = list(envs) + list(self._envs)
|
234
|
+
for checker in self._get_checkers():
|
235
|
+
checker.insert_env(*envs)
|
222
236
|
|
223
237
|
def add_env(self, *envs: Env):
|
224
238
|
if not self.__allow_add_envs:
|
225
239
|
raise Exception(f"Cannot add envs to `{self.get_name()}`")
|
226
240
|
self._envs = list(self._envs) + list(envs)
|
241
|
+
for checker in self._get_checkers():
|
242
|
+
checker.add_env(*envs)
|
227
243
|
|
228
244
|
def inject_envs(self):
|
229
245
|
pass
|
@@ -255,11 +271,15 @@ class CommonTaskModel:
|
|
255
271
|
if not self.__allow_add_env_files:
|
256
272
|
raise Exception(f"Cannot insert env_files to `{self.get_name()}`")
|
257
273
|
self._env_files = list(env_files) + list(self._env_files)
|
274
|
+
for checker in self._get_checkers():
|
275
|
+
checker.insert_env_file(*env_files)
|
258
276
|
|
259
277
|
def add_env_file(self, *env_files: EnvFile):
|
260
278
|
if not self.__allow_add_env_files:
|
261
279
|
raise Exception(f"Cannot add env_files to `{self.get_name()}`")
|
262
280
|
self._env_files = list(self._env_files) + list(env_files)
|
281
|
+
for checker in self._get_checkers():
|
282
|
+
checker.add_env_file(*env_files)
|
263
283
|
|
264
284
|
def inject_env_files(self):
|
265
285
|
pass
|
@@ -317,15 +337,26 @@ class CommonTaskModel:
|
|
317
337
|
def insert_checker(self, *checkers: AnyTask):
|
318
338
|
if not self.__allow_add_checkers:
|
319
339
|
raise Exception(f"Cannot insert checkers to `{self.get_name()}`")
|
320
|
-
additional_checkers =
|
340
|
+
additional_checkers = self.__complete_new_checkers(checkers)
|
321
341
|
self._checkers = additional_checkers + self._checkers
|
322
342
|
|
323
343
|
def add_checker(self, *checkers: AnyTask):
|
324
344
|
if not self.__allow_add_checkers:
|
325
345
|
raise Exception(f"Cannot add checkers to `{self.get_name()}`")
|
326
|
-
additional_checkers =
|
346
|
+
additional_checkers = self.__complete_new_checkers(checkers)
|
327
347
|
self._checkers = self._checkers + additional_checkers
|
328
348
|
|
349
|
+
def __complete_new_checkers(self, new_checkers: List[AnyTask]) -> List[AnyTask]:
|
350
|
+
"""
|
351
|
+
For internal use: copy and completing new checkers
|
352
|
+
"""
|
353
|
+
checkers: List[AnyTask] = [checker.copy() for checker in new_checkers]
|
354
|
+
for checker in checkers:
|
355
|
+
checker.add_input(*self._get_inputs())
|
356
|
+
checker.add_env(*self._get_envs())
|
357
|
+
checker.add_env_file(*self._get_env_files())
|
358
|
+
return checkers
|
359
|
+
|
329
360
|
def inject_checkers(self):
|
330
361
|
pass
|
331
362
|
|
zrb/task/checker.py
CHANGED
@@ -44,7 +44,7 @@ class Checker(BaseTask):
|
|
44
44
|
on_ready: Optional[OnReady] = None,
|
45
45
|
on_retry: Optional[OnRetry] = None,
|
46
46
|
on_failed: Optional[OnFailed] = None,
|
47
|
-
checking_interval: Union[int, float] = 0,
|
47
|
+
checking_interval: Union[int, float] = 0.05,
|
48
48
|
progress_interval: Union[int, float] = 30,
|
49
49
|
expected_result: bool = True,
|
50
50
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
zrb/task/cmd_task.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import asyncio
|
2
2
|
import atexit
|
3
|
+
import logging
|
3
4
|
import os
|
4
5
|
import pathlib
|
5
6
|
import signal
|
@@ -7,7 +8,7 @@ import subprocess
|
|
7
8
|
import sys
|
8
9
|
import time
|
9
10
|
|
10
|
-
from zrb.config.config import default_shell
|
11
|
+
from zrb.config.config import default_shell, logging_level
|
11
12
|
from zrb.helper.accessories.color import colored
|
12
13
|
from zrb.helper.log import logger
|
13
14
|
from zrb.helper.typecheck import typechecked
|
@@ -55,6 +56,18 @@ def _reset_stty():
|
|
55
56
|
_has_stty = False
|
56
57
|
|
57
58
|
|
59
|
+
def _log_error(message: Any):
|
60
|
+
if logging_level > logging.ERROR:
|
61
|
+
return
|
62
|
+
colored_message = colored(f"{message}", color="red", attrs=["bold"])
|
63
|
+
logger.error(colored_message, exc_info=True)
|
64
|
+
|
65
|
+
|
66
|
+
def _print_out_dark(message: Any):
|
67
|
+
message_str = f"{message}"
|
68
|
+
print(colored(message_str, attrs=["dark"]), file=sys.stderr)
|
69
|
+
|
70
|
+
|
58
71
|
CmdVal = Union[
|
59
72
|
JinjaTemplate,
|
60
73
|
Iterable[JinjaTemplate],
|
@@ -125,7 +138,7 @@ class CmdTask(BaseTask):
|
|
125
138
|
on_retry: Optional[OnRetry] = None,
|
126
139
|
on_failed: Optional[OnFailed] = None,
|
127
140
|
checkers: Iterable[AnyTask] = [],
|
128
|
-
checking_interval: Union[float, int] = 0,
|
141
|
+
checking_interval: Union[float, int] = 0.05,
|
129
142
|
retry: int = 2,
|
130
143
|
retry_interval: Union[float, int] = 1,
|
131
144
|
max_output_line: int = 1000,
|
@@ -272,7 +285,7 @@ class CmdTask(BaseTask):
|
|
272
285
|
def __on_kill(self, signum: Any, frame: Any):
|
273
286
|
self._global_state.no_more_attempt = True
|
274
287
|
self._global_state.is_killed_by_signal = True
|
275
|
-
|
288
|
+
_print_out_dark(f"Getting signal {signum}")
|
276
289
|
for pid in self._pids:
|
277
290
|
self.__kill_by_pid(pid)
|
278
291
|
tasks = asyncio.all_tasks()
|
@@ -282,7 +295,7 @@ class CmdTask(BaseTask):
|
|
282
295
|
except Exception as e:
|
283
296
|
self.print_err(e)
|
284
297
|
time.sleep(0.3)
|
285
|
-
|
298
|
+
_print_out_dark(f"Exiting with signal {signum}")
|
286
299
|
sys.exit(signum)
|
287
300
|
|
288
301
|
def __on_exit(self):
|
@@ -297,20 +310,20 @@ class CmdTask(BaseTask):
|
|
297
310
|
process_ever_exists = False
|
298
311
|
if self.__is_process_exist(pid):
|
299
312
|
process_ever_exists = True
|
300
|
-
|
313
|
+
_print_out_dark(f"Send SIGTERM to process {pid}")
|
301
314
|
os.killpg(os.getpgid(pid), signal.SIGTERM)
|
302
315
|
time.sleep(0.3)
|
303
316
|
if self.__is_process_exist(pid):
|
304
|
-
|
317
|
+
_print_out_dark(f"Send SIGINT to process {pid}")
|
305
318
|
os.killpg(os.getpgid(pid), signal.SIGINT)
|
306
319
|
time.sleep(0.3)
|
307
320
|
if self.__is_process_exist(pid):
|
308
|
-
|
321
|
+
_print_out_dark(f"Send SIGKILL to process {pid}")
|
309
322
|
os.killpg(os.getpgid(pid), signal.SIGKILL)
|
310
323
|
if process_ever_exists:
|
311
|
-
|
324
|
+
_print_out_dark(f"Process {pid} is killed successfully")
|
312
325
|
except Exception:
|
313
|
-
|
326
|
+
_log_error(f"Cannot kill process {pid}")
|
314
327
|
|
315
328
|
def __is_process_exist(self, pid: int) -> bool:
|
316
329
|
try:
|
zrb/task/decorator.py
CHANGED
@@ -43,7 +43,7 @@ def python_task(
|
|
43
43
|
on_retry: Optional[OnRetry] = None,
|
44
44
|
on_failed: Optional[OnFailed] = None,
|
45
45
|
checkers: Iterable[AnyTask] = [],
|
46
|
-
checking_interval: Union[float, int] = 0,
|
46
|
+
checking_interval: Union[float, int] = 0.05,
|
47
47
|
retry: int = 2,
|
48
48
|
retry_interval: Union[float, int] = 1,
|
49
49
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
zrb/task/docker_compose_task.py
CHANGED
@@ -115,7 +115,7 @@ class DockerComposeTask(CmdTask):
|
|
115
115
|
on_retry: Optional[OnRetry] = None,
|
116
116
|
on_failed: Optional[OnFailed] = None,
|
117
117
|
checkers: Iterable[AnyTask] = [],
|
118
|
-
checking_interval: Union[float, int] = 0,
|
118
|
+
checking_interval: Union[float, int] = 0.05,
|
119
119
|
retry: int = 2,
|
120
120
|
retry_interval: Union[float, int] = 1,
|
121
121
|
max_output_line: int = 1000,
|
zrb/task/flow_task.py
CHANGED
@@ -45,27 +45,13 @@ class FlowTask(BaseTask):
|
|
45
45
|
on_retry: Optional[OnRetry] = None,
|
46
46
|
on_failed: Optional[OnFailed] = None,
|
47
47
|
checkers: Iterable[AnyTask] = [],
|
48
|
-
checking_interval: float = 0,
|
48
|
+
checking_interval: Union[float, int] = 0.05,
|
49
49
|
retry: int = 2,
|
50
|
-
retry_interval: float = 1,
|
50
|
+
retry_interval: Union[float, int] = 1,
|
51
51
|
steps: List[Union[AnyTask, List[AnyTask]]] = [],
|
52
52
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
53
53
|
return_upstream_result: bool = False,
|
54
54
|
):
|
55
|
-
final_upstreams: List[AnyTask] = list(upstreams)
|
56
|
-
inputs: List[AnyInput] = list(inputs)
|
57
|
-
envs: List[Env] = list(envs)
|
58
|
-
env_files: List[EnvFile] = list(env_files)
|
59
|
-
for step in steps:
|
60
|
-
tasks = self._step_to_tasks(step)
|
61
|
-
new_upstreams = self._get_embeded_tasks(
|
62
|
-
tasks=tasks,
|
63
|
-
upstreams=final_upstreams,
|
64
|
-
inputs=inputs,
|
65
|
-
envs=envs,
|
66
|
-
env_files=env_files,
|
67
|
-
)
|
68
|
-
final_upstreams = new_upstreams
|
69
55
|
BaseTask.__init__(
|
70
56
|
self,
|
71
57
|
name=name,
|
@@ -76,7 +62,13 @@ class FlowTask(BaseTask):
|
|
76
62
|
icon=icon,
|
77
63
|
color=color,
|
78
64
|
description=description,
|
79
|
-
upstreams=
|
65
|
+
upstreams=self._create_flow_upstreams(
|
66
|
+
steps=steps,
|
67
|
+
upstreams=list(upstreams),
|
68
|
+
inputs=list(inputs),
|
69
|
+
envs=list(envs),
|
70
|
+
env_files=list(env_files),
|
71
|
+
),
|
80
72
|
fallbacks=fallbacks,
|
81
73
|
on_triggered=on_triggered,
|
82
74
|
on_waiting=on_waiting,
|
@@ -97,12 +89,33 @@ class FlowTask(BaseTask):
|
|
97
89
|
def copy(self) -> TFlowTask:
|
98
90
|
return super().copy()
|
99
91
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
92
|
+
def _create_flow_upstreams(
|
93
|
+
self,
|
94
|
+
steps: List[Union[AnyTask, List[AnyTask]]],
|
95
|
+
upstreams: List[AnyTask],
|
96
|
+
inputs: List[AnyInput],
|
97
|
+
envs: List[Env],
|
98
|
+
env_files: List[EnvFile],
|
99
|
+
) -> List[AnyTask]:
|
100
|
+
flow_upstreams = upstreams
|
101
|
+
for step in steps:
|
102
|
+
tasks = [task.copy() for task in self._step_to_tasks(step)]
|
103
|
+
new_upstreams = self._create_embeded_tasks(
|
104
|
+
tasks=tasks,
|
105
|
+
upstreams=flow_upstreams,
|
106
|
+
inputs=inputs,
|
107
|
+
envs=envs,
|
108
|
+
env_files=env_files,
|
109
|
+
)
|
110
|
+
flow_upstreams = new_upstreams
|
111
|
+
return flow_upstreams
|
112
|
+
|
113
|
+
def _step_to_tasks(self, step: Union[AnyTask, List[AnyTask]]) -> List[AnyTask]:
|
114
|
+
if isinstance(step, AnyTask):
|
115
|
+
return [step]
|
116
|
+
return step
|
104
117
|
|
105
|
-
def
|
118
|
+
def _create_embeded_tasks(
|
106
119
|
self,
|
107
120
|
tasks: List[AnyTask],
|
108
121
|
upstreams: List[AnyTask],
|
@@ -111,28 +124,26 @@ class FlowTask(BaseTask):
|
|
111
124
|
env_files: List[EnvFile],
|
112
125
|
) -> List[AnyTask]:
|
113
126
|
embeded_tasks: List[AnyTask] = []
|
114
|
-
for
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
embeded_task_root_upstream.add_upstream(*upstreams)
|
119
|
-
# embeded_task.add_upstream(*upstreams)
|
127
|
+
for embeded_task in tasks:
|
128
|
+
embeded_task_upstreams = self._get_all_upstreams(tasks=[embeded_task])
|
129
|
+
for embeded_task_upstream in embeded_task_upstreams:
|
130
|
+
embeded_task_upstream.add_upstream(*upstreams)
|
120
131
|
embeded_task.add_env(*envs)
|
121
132
|
embeded_task.add_env_file(*env_files)
|
122
133
|
embeded_task.add_input(*inputs)
|
123
134
|
embeded_tasks.append(embeded_task)
|
124
135
|
return embeded_tasks
|
125
136
|
|
126
|
-
def
|
127
|
-
|
137
|
+
def _get_all_upstreams(self, tasks: List[AnyTask]):
|
138
|
+
all_upstreams = []
|
128
139
|
for task in tasks:
|
129
140
|
upstreams = task._get_upstreams()
|
130
141
|
if len(upstreams) == 0:
|
131
|
-
|
142
|
+
all_upstreams.append(task)
|
132
143
|
continue
|
133
144
|
for upstream in upstreams:
|
134
145
|
if len(upstream._get_upstreams()) == 0:
|
135
|
-
|
146
|
+
all_upstreams.append(upstream)
|
136
147
|
continue
|
137
|
-
|
138
|
-
return
|
148
|
+
all_upstreams += self._get_all_upstreams([upstream])
|
149
|
+
return all_upstreams
|
zrb/task/http_checker.py
CHANGED
@@ -87,7 +87,7 @@ class HTTPChecker(Checker):
|
|
87
87
|
on_ready: Optional[OnReady] = None,
|
88
88
|
on_retry: Optional[OnRetry] = None,
|
89
89
|
on_failed: Optional[OnFailed] = None,
|
90
|
-
checking_interval: Union[int, float] = 0,
|
90
|
+
checking_interval: Union[int, float] = 0.05,
|
91
91
|
progress_interval: Union[int, float] = 5,
|
92
92
|
expected_result: bool = True,
|
93
93
|
should_execute: Union[bool, JinjaTemplate, Callable[..., bool]] = True,
|
zrb/task/notifier.py
CHANGED
@@ -58,7 +58,7 @@ class Notifier(BaseTask):
|
|
58
58
|
on_ready: Optional[OnReady] = None,
|
59
59
|
on_retry: Optional[OnRetry] = None,
|
60
60
|
on_failed: Optional[OnFailed] = None,
|
61
|
-
checking_interval: Union[int, float] = 0,
|
61
|
+
checking_interval: Union[int, float] = 0.05,
|
62
62
|
retry: int = 2,
|
63
63
|
retry_interval: Union[float, int] = 1,
|
64
64
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
zrb/task/path_checker.py
CHANGED
@@ -56,7 +56,7 @@ class PathChecker(Checker):
|
|
56
56
|
on_failed: Optional[OnFailed] = None,
|
57
57
|
path: JinjaTemplate = "",
|
58
58
|
ignored_path: Union[JinjaTemplate, Iterable[JinjaTemplate]] = [],
|
59
|
-
checking_interval: Union[int, float] = 0,
|
59
|
+
checking_interval: Union[int, float] = 0.05,
|
60
60
|
progress_interval: Union[int, float] = 5,
|
61
61
|
expected_result: bool = True,
|
62
62
|
should_execute: Union[bool, JinjaTemplate, Callable[..., bool]] = True,
|
zrb/task/path_watcher.py
CHANGED
@@ -72,7 +72,7 @@ class PathWatcher(Watcher):
|
|
72
72
|
on_failed: Optional[OnFailed] = None,
|
73
73
|
path: JinjaTemplate = "",
|
74
74
|
ignored_path: Union[JinjaTemplate, Iterable[JinjaTemplate]] = [],
|
75
|
-
checking_interval: Union[int, float] = 0,
|
75
|
+
checking_interval: Union[int, float] = 0.05,
|
76
76
|
progress_interval: Union[int, float] = 30,
|
77
77
|
watch_new_files: bool = True,
|
78
78
|
watch_modified_files: bool = True,
|
zrb/task/port_checker.py
CHANGED
@@ -66,7 +66,7 @@ class PortChecker(Checker):
|
|
66
66
|
on_ready: Optional[OnReady] = None,
|
67
67
|
on_retry: Optional[OnRetry] = None,
|
68
68
|
on_failed: Optional[OnFailed] = None,
|
69
|
-
checking_interval: Union[int, float] = 0,
|
69
|
+
checking_interval: Union[int, float] = 0.05,
|
70
70
|
progress_interval: Union[int, float] = 5,
|
71
71
|
expected_result: bool = True,
|
72
72
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
zrb/task/recurring_task.py
CHANGED
@@ -76,7 +76,7 @@ class RecurringTask(BaseTask):
|
|
76
76
|
on_retry: Optional[OnRetry] = None,
|
77
77
|
on_failed: Optional[OnFailed] = None,
|
78
78
|
checkers: Iterable[AnyTask] = [],
|
79
|
-
checking_interval: float = 0,
|
79
|
+
checking_interval: float = 0.05,
|
80
80
|
retry: int = 0,
|
81
81
|
retry_interval: float = 1,
|
82
82
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
@@ -185,7 +185,7 @@ class RecurringTask(BaseTask):
|
|
185
185
|
async def __run_from_queue(self):
|
186
186
|
while True:
|
187
187
|
if len(self._run_configs) == 0:
|
188
|
-
await asyncio.sleep(0.
|
188
|
+
await asyncio.sleep(0.05)
|
189
189
|
continue
|
190
190
|
if self._single_execution:
|
191
191
|
# Drain the queue, leave only the latest task
|
zrb/task/remote_cmd_task.py
CHANGED
@@ -66,7 +66,7 @@ class RemoteCmdTask(BaseRemoteCmdTask):
|
|
66
66
|
on_retry: Optional[OnRetry] = None,
|
67
67
|
on_failed: Optional[OnFailed] = None,
|
68
68
|
checkers: Iterable[AnyTask] = [],
|
69
|
-
checking_interval: Union[float, int] = 0,
|
69
|
+
checking_interval: Union[float, int] = 0.05,
|
70
70
|
retry: int = 2,
|
71
71
|
retry_interval: Union[float, int] = 1,
|
72
72
|
max_output_line: int = 1000,
|
zrb/task/resource_maker.py
CHANGED
zrb/task/rsync_task.py
CHANGED
@@ -48,8 +48,8 @@ class RsyncTask(BaseRemoteCmdTask):
|
|
48
48
|
remote_configs: Iterable[RemoteConfig],
|
49
49
|
src: JinjaTemplate,
|
50
50
|
dst: JinjaTemplate,
|
51
|
-
|
52
|
-
|
51
|
+
src_is_remote: bool = False,
|
52
|
+
dst_is_remote: bool = True,
|
53
53
|
group: Optional[Group] = None,
|
54
54
|
inputs: Iterable[AnyInput] = [],
|
55
55
|
envs: Iterable[Env] = [],
|
@@ -69,7 +69,7 @@ class RsyncTask(BaseRemoteCmdTask):
|
|
69
69
|
on_retry: Optional[OnRetry] = None,
|
70
70
|
on_failed: Optional[OnFailed] = None,
|
71
71
|
checkers: Iterable[AnyTask] = [],
|
72
|
-
checking_interval: Union[float, int] = 0,
|
72
|
+
checking_interval: Union[float, int] = 0.05,
|
73
73
|
retry: int = 2,
|
74
74
|
retry_interval: Union[float, int] = 1,
|
75
75
|
max_output_line: int = 1000,
|
@@ -77,8 +77,8 @@ class RsyncTask(BaseRemoteCmdTask):
|
|
77
77
|
preexec_fn: Optional[Callable[[], Any]] = os.setsid,
|
78
78
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
79
79
|
):
|
80
|
-
parsed_src = self._get_parsed_path(
|
81
|
-
parsed_dst = self._get_parsed_path(
|
80
|
+
parsed_src = self._get_parsed_path(src_is_remote, src)
|
81
|
+
parsed_dst = self._get_parsed_path(dst_is_remote, dst)
|
82
82
|
cmd = f'auth_rsync "{parsed_src}" "{parsed_dst}"'
|
83
83
|
BaseRemoteCmdTask.__init__(
|
84
84
|
self,
|
zrb/task/server.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import asyncio
|
2
|
+
import copy
|
2
3
|
|
3
4
|
from zrb.helper.accessories.color import colored
|
4
5
|
from zrb.helper.accessories.name import get_random_name
|
@@ -35,8 +36,8 @@ class Controller:
|
|
35
36
|
name: Optional[str] = None,
|
36
37
|
):
|
37
38
|
self._name = get_random_name() if name is None else name
|
38
|
-
self._triggers =
|
39
|
-
self._actions =
|
39
|
+
self._triggers = self._to_task_list(trigger)
|
40
|
+
self._actions = self._to_task_list(action)
|
40
41
|
self._args: List[Any] = []
|
41
42
|
self._kwargs: Mapping[str, Any] = {}
|
42
43
|
self._inputs: List[AnyInput] = []
|
@@ -58,28 +59,28 @@ class Controller:
|
|
58
59
|
def set_env_files(self, env_files: List[EnvFile]):
|
59
60
|
self._env_files = env_files
|
60
61
|
|
61
|
-
def
|
62
|
+
def get_original_env_files(self) -> Iterable[EnvFile]:
|
62
63
|
env_files = []
|
63
64
|
for trigger in self._triggers:
|
64
|
-
env_files += trigger.
|
65
|
+
env_files += trigger._get_env_files()
|
65
66
|
for action in self._actions:
|
66
|
-
env_files += action.
|
67
|
+
env_files += action._get_env_files()
|
67
68
|
return env_files
|
68
69
|
|
69
|
-
def
|
70
|
+
def get_original_envs(self) -> Iterable[Env]:
|
70
71
|
envs = []
|
71
72
|
for trigger in self._triggers:
|
72
|
-
envs += trigger.
|
73
|
+
envs += trigger._get_envs()
|
73
74
|
for action in self._actions:
|
74
|
-
envs += action.
|
75
|
+
envs += action._get_envs()
|
75
76
|
return envs
|
76
77
|
|
77
|
-
def
|
78
|
+
def get_original_inputs(self) -> Iterable[AnyInput]:
|
78
79
|
inputs = []
|
79
80
|
for trigger in self._triggers:
|
80
|
-
inputs += trigger.
|
81
|
+
inputs += trigger._get_combined_inputs()
|
81
82
|
for action in self._actions:
|
82
|
-
inputs += action.
|
83
|
+
inputs += action._get_combined_inputs()
|
83
84
|
return inputs
|
84
85
|
|
85
86
|
def to_function(self) -> Callable[..., Any]:
|
@@ -92,6 +93,11 @@ class Controller:
|
|
92
93
|
|
93
94
|
return fn
|
94
95
|
|
96
|
+
def _to_task_list(self, tasks: Union[AnyTask, List[AnyTask]]) -> List[AnyTask]:
|
97
|
+
if isinstance(tasks, AnyTask):
|
98
|
+
return [tasks.copy()]
|
99
|
+
return [task.copy() for task in tasks]
|
100
|
+
|
95
101
|
def _get_task(self) -> AnyTask:
|
96
102
|
actions = [action.copy() for action in self._actions]
|
97
103
|
actions.insert(0, self._get_remonitor_task())
|
@@ -123,7 +129,7 @@ class Server(BaseTask):
|
|
123
129
|
def __init__(
|
124
130
|
self,
|
125
131
|
name: str,
|
126
|
-
controllers:
|
132
|
+
controllers: Iterable[Controller],
|
127
133
|
group: Optional[Group] = None,
|
128
134
|
inputs: Iterable[AnyInput] = [],
|
129
135
|
envs: Iterable[Env] = [],
|
@@ -141,17 +147,18 @@ class Server(BaseTask):
|
|
141
147
|
on_retry: Optional[OnRetry] = None,
|
142
148
|
on_failed: Optional[OnFailed] = None,
|
143
149
|
checkers: Iterable[AnyTask] = [],
|
144
|
-
checking_interval: float = 0,
|
150
|
+
checking_interval: Union[int, float] = 0.05,
|
145
151
|
retry: int = 0,
|
146
|
-
retry_interval: float = 1,
|
152
|
+
retry_interval: Union[int, float] = 1,
|
147
153
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
148
154
|
return_upstream_result: bool = False,
|
149
155
|
):
|
150
156
|
inputs, envs, env_files = list(inputs), list(envs), list(env_files)
|
151
157
|
for controller in controllers:
|
152
|
-
|
153
|
-
|
154
|
-
|
158
|
+
controller_cp = copy.deepcopy(controller)
|
159
|
+
inputs += controller_cp.get_original_inputs()
|
160
|
+
envs += controller_cp.get_original_envs()
|
161
|
+
env_files += controller_cp.get_original_env_files()
|
155
162
|
BaseTask.__init__(
|
156
163
|
self,
|
157
164
|
name=name,
|
@@ -178,7 +185,7 @@ class Server(BaseTask):
|
|
178
185
|
should_execute=should_execute,
|
179
186
|
return_upstream_result=return_upstream_result,
|
180
187
|
)
|
181
|
-
self._controllers = controllers
|
188
|
+
self._controllers = list(controllers)
|
182
189
|
|
183
190
|
async def run(self, *args: Any, **kwargs: Any):
|
184
191
|
for controller in self._controllers:
|
zrb/task/task.py
CHANGED
@@ -9,7 +9,41 @@ logger.debug(colored("Loading zrb.task.task", attrs=["dark"]))
|
|
9
9
|
@typechecked
|
10
10
|
class Task(BaseTask):
|
11
11
|
"""
|
12
|
-
|
12
|
+
Task is the smallest Zrb automation unit.
|
13
|
+
|
14
|
+
You can configure a Task by using several interfaces:
|
15
|
+
- `inputs`: interfaces to read user input at the beginning of the execution.
|
16
|
+
- `envs`: interfaces to read and use OS Environment Variables.
|
17
|
+
- `env_files`: interfaces to read and use Environment Files.
|
18
|
+
|
19
|
+
Moreover, you can define Task dependencies by specifying its `upstreams` or by using shift-right operator.
|
20
|
+
|
21
|
+
Every Zrb Task has its life-cycle state:
|
22
|
+
- `Triggered`: The Task is triggered (either by the user or by the other Task).
|
23
|
+
- `Waiting`: Zrb has already triggered the Task. The Task is now waiting for all its upstreams to be ready.
|
24
|
+
- `Skipped`: Task upstreams are ready, but the Task is not executed and will immediately enter the `Ready` state.
|
25
|
+
- `Started`: The upstreams are ready, and Zrb is now starting the Task execution.
|
26
|
+
- `Failed`: Zrb failed to execute the Task. It will enter the `Retry` state if the current attempt does not exceed the maximum attempt.
|
27
|
+
- `Retry`: The task has already entered the `Failed` state. Now, Zrb will try to start the Task execution.
|
28
|
+
- `Ready`: The task is ready.
|
29
|
+
|
30
|
+
There are several configurations related to Task's life cycle:
|
31
|
+
- `retry`: Maximum retry attempt.
|
32
|
+
- `retry_interval`: The duration is to wait before Zrb starts the next attempt.
|
33
|
+
- `fallbacks`: Action to take if the Task has failed for good.
|
34
|
+
- `checkers`: How to determine if a Task is `Ready`.
|
35
|
+
- `checking_interval`: The duration to wait before Zrb checks for the Task's readiness.
|
36
|
+
- `run`: Action to do when Zrb executes the Task.
|
37
|
+
- `on_triggered`: Action to do when a Task is `Triggered`.
|
38
|
+
- `on_waiting`: Action to do when a Task is `Waiting`.
|
39
|
+
- `on_skipped`: Action to do when a Task is `Skipped`.
|
40
|
+
- `on_started`: Action to do when a Task is `Started`.
|
41
|
+
- `on_ready`: Action to do when a Task is `Ready`.
|
42
|
+
- `on_retry`: Action to do when a Task is `Retry`.
|
43
|
+
- `on_failed`: Action to do when a Task is `Failed`.
|
44
|
+
- `should_execute`: Condition to determine whether a Task should be `Started` or `Skipped`.
|
45
|
+
|
46
|
+
Finally, you can put related Tasks under the same `group`.
|
13
47
|
"""
|
14
48
|
|
15
49
|
pass
|
zrb/task/time_watcher.py
CHANGED
@@ -68,7 +68,7 @@ class TimeWatcher(Watcher):
|
|
68
68
|
on_retry: Optional[OnRetry] = None,
|
69
69
|
on_failed: Optional[OnFailed] = None,
|
70
70
|
schedule: JinjaTemplate = "",
|
71
|
-
checking_interval: Union[int, float] = 0,
|
71
|
+
checking_interval: Union[int, float] = 0.05,
|
72
72
|
progress_interval: Union[int, float] = 30,
|
73
73
|
should_execute: Union[bool, JinjaTemplate, Callable[..., bool]] = True,
|
74
74
|
):
|
zrb/task/watcher.py
CHANGED
@@ -2,6 +2,7 @@ import asyncio
|
|
2
2
|
|
3
3
|
from zrb.helper.accessories.color import colored
|
4
4
|
from zrb.helper.accessories.name import get_random_name
|
5
|
+
from zrb.helper.callable import run_async
|
5
6
|
from zrb.helper.log import logger
|
6
7
|
from zrb.helper.typecheck import typechecked
|
7
8
|
from zrb.helper.typing import Any, Callable, Iterable, Optional, Union
|
@@ -48,7 +49,7 @@ class Watcher(Checker):
|
|
48
49
|
on_ready: Optional[OnReady] = None,
|
49
50
|
on_retry: Optional[OnRetry] = None,
|
50
51
|
on_failed: Optional[OnFailed] = None,
|
51
|
-
checking_interval: Union[int, float] = 0,
|
52
|
+
checking_interval: Union[int, float] = 0.05,
|
52
53
|
progress_interval: Union[int, float] = 30,
|
53
54
|
expected_result: bool = True,
|
54
55
|
should_execute: Union[bool, str, Callable[..., bool]] = True,
|
@@ -85,7 +86,9 @@ class Watcher(Checker):
|
|
85
86
|
async def run(self, *args: Any, **kwargs: Any) -> bool:
|
86
87
|
if not looper.is_registered(self._identifier):
|
87
88
|
asyncio.create_task(
|
88
|
-
|
89
|
+
run_async(
|
90
|
+
looper.register, self._identifier, self.create_loop_inspector()
|
91
|
+
)
|
89
92
|
)
|
90
93
|
return await super().run(*args, **kwargs)
|
91
94
|
|