zrb 0.0.108__py3-none-any.whl → 0.0.110__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/action/runner.py CHANGED
@@ -27,10 +27,10 @@ class Runner():
27
27
  logger.info(colored('Runner created', attrs=['dark']))
28
28
 
29
29
  def register(self, task: AnyTask):
30
- cmd_name = task.get_complete_cmd_name()
30
+ task.set_has_cli_interface()
31
+ cmd_name = task.get_full_cmd_name()
31
32
  logger.debug(colored(f'Register task: {cmd_name}', attrs=['dark']))
32
33
  self._tasks.append(task)
33
- task.set_has_cli_interface()
34
34
  logger.debug(colored(f'Task registered: {cmd_name}', attrs=['dark']))
35
35
 
36
36
  def serve(self, cli: click.Group) -> click.Group:
@@ -77,7 +77,7 @@ class Runner():
77
77
  return group
78
78
 
79
79
  def _create_cli_command(self, task: AnyTask) -> click.Command:
80
- task_inputs = task.get_all_inputs()
80
+ task_inputs = task._get_combined_inputs()
81
81
  task_cmd_name = task.get_cmd_name()
82
82
  task_description = task.get_description()
83
83
  task_function = task.to_function(
zrb/builtin/git.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from zrb.helper.typing import Any
2
2
  from zrb.builtin.group import git_group
3
3
  from zrb.task.decorator import python_task
4
- from zrb.task.task import Task
5
4
  from zrb.task_input.str_input import StrInput
6
5
  from zrb.task_input.bool_input import BoolInput
7
6
  from zrb.runner import runner
@@ -26,7 +25,7 @@ from zrb.helper.python_task import show_lines
26
25
  prompt='Commit hash/Tag',
27
26
  default='HEAD'
28
27
  ),
29
- BoolInput(
28
+ BoolInput(
30
29
  name='include-new',
31
30
  description='include new files',
32
31
  prompt='Include new files',
@@ -52,7 +51,6 @@ async def get_file_changes(*args: Any, **kwargs: Any):
52
51
  include_new = kwargs.get('include_new', True)
53
52
  include_removed = kwargs.get('include_removed', True)
54
53
  include_updated = kwargs.get('include_updated', True)
55
- task: Task = kwargs['_task']
56
54
  modified_file_states = get_modified_file_states(commit)
57
55
  modified_file_keys = []
58
56
  output = []
@@ -10,19 +10,29 @@ def get_random_name(
10
10
  digit_count: int = 4
11
11
  ) -> str:
12
12
  prefixes = [
13
- "aurum", "argentum", "platinum", "mercurius", "sulfur", "sal",
14
- "luna", "sol", "ferrum", "cuprum", "argent", "aurora", "citrin",
15
- "coral", "diamond", "dragon", "emerald", "garnet", "jade", "onyx",
16
- "opal", "pearl", "ruby", "sapphire", "topaz", "turquoise", "verde",
17
- "zircon"
13
+ 'albedo', 'argent', 'argentum', 'aurora', 'aurum', 'azure',
14
+ 'basilisk', 'cerulean', 'chimeric', 'citrin', 'coral', 'crimson',
15
+ 'diamond', 'draco', 'dragon', 'emerald', 'ethereal', 'ferrum',
16
+ 'flammeus', 'garnet', 'glacial', 'glimmering', 'glistening', 'golden',
17
+ 'helios', 'igneous', 'imperial', 'jade', 'luminous', 'luna', 'lunar',
18
+ 'mystic', 'nephrite', 'nocturnal', 'obsidian', 'opal', 'pearl',
19
+ 'platinum', 'prismatic', 'ruby', 'sapphire', 'serpentine', 'silver',
20
+ 'sol', 'solar', 'spiritual', 'stellar', 'tempest', 'topaz',
21
+ 'turquoise', 'verde', 'vermillion', 'vitreous', 'zephyr', 'zircon'
18
22
  ]
19
23
  suffixes = [
20
- "philosophorum", "spiritus", "tinctura", "essentia", "elixir",
21
- "praeparatum", "aether", "vitae", "lapis", "metallum", "aureum",
22
- "caelestis", "chrysopoeia", "cosmicum", "deum", "draconis",
23
- "elementorum", "hermetica", "illuminationis", "magnum", "mysticum",
24
- "occultum", "omnipotentis", "philosophia", "praestantissimum",
25
- "quintessentia", "regeneratio", "universalis"
24
+ 'aether', 'albedo', 'alchemy', 'arcana', 'aureum', 'aetheris',
25
+ 'anima', 'astralis', 'caelestis', 'chrysopoeia', 'cosmicum',
26
+ 'crystallum', 'deum', 'divinitas', 'draconis', 'elementorum', 'elixir',
27
+ 'essentia', 'eternis', 'ethereus', 'fatum', 'flamma', 'fulgur',
28
+ 'hermetica', 'ignis', 'illuminationis', 'imperium', 'incantatum',
29
+ 'infinitum', 'lapis', 'lux', 'magicae', 'magnum', 'materia',
30
+ 'metallum', 'mysticum', 'natura', 'occultum', 'omnipotentis',
31
+ 'opulentia', 'philosophia', 'philosophorum', 'praeparatum',
32
+ 'praestantissimum', 'prima', 'primordium', 'quintessentia',
33
+ 'regeneratio', 'ritualis', 'sanctum', 'spiritus', 'tenebris',
34
+ 'terra', 'tinctura', 'transmutationis', 'universalis', 'vapores',
35
+ 'venenum', 'veritas', 'vitae', 'volatus'
26
36
  ]
27
37
  prefix = random.choice(prefixes)
28
38
  suffix = random.choice(suffixes)
@@ -16,7 +16,7 @@ def fetch_env_map_from_group(
16
16
  sub_env_map: Mapping[str, str] = fetch_env_map_from_group(
17
17
  env_map, sub_group
18
18
  )
19
- env_map = cascade_env_map(env_map, sub_env_map)
19
+ env_map = _cascade_env_map(env_map, sub_env_map)
20
20
  return env_map
21
21
 
22
22
 
@@ -25,33 +25,33 @@ def fetch_env_map_from_task(
25
25
  env_map: Mapping[str, str], task: AnyTask
26
26
  ):
27
27
  task_env_map: Mapping[str, str] = {}
28
- for env_file in task.get_env_files():
28
+ for env_file in task._get_env_files():
29
29
  envs = env_file.get_envs()
30
- task_env_map = add_envs_to_env_map(task_env_map, envs)
31
- task_env_map = add_envs_to_env_map(task_env_map, task._envs)
32
- env_map = cascade_env_map(env_map, task_env_map)
33
- for upstream in task.get_upstreams():
30
+ task_env_map = _add_envs_to_env_map(task_env_map, envs)
31
+ task_env_map = _add_envs_to_env_map(task_env_map, task._envs)
32
+ env_map = _cascade_env_map(env_map, task_env_map)
33
+ for upstream in task._get_upstreams():
34
34
  task_env_map = fetch_env_map_from_task(env_map, upstream)
35
- for checker in task.get_checkers():
35
+ for checker in task._get_checkers():
36
36
  task_env_map = fetch_env_map_from_task(env_map, checker)
37
37
  return env_map
38
38
 
39
39
 
40
40
  @typechecked
41
- def add_envs_to_env_map(
41
+ def _add_envs_to_env_map(
42
42
  env_map: Mapping[str, str], envs: List[Env]
43
43
  ) -> Mapping[str, str]:
44
44
  for env in envs:
45
45
  if env.os_name == '':
46
46
  continue
47
- env_name = get_env_name(env)
48
- env_default = get_env_default(env)
47
+ env_name = _get_env_name(env)
48
+ env_default = _get_env_default(env)
49
49
  env_map[env_name] = env_default
50
50
  return env_map
51
51
 
52
52
 
53
53
  @typechecked
54
- def cascade_env_map(
54
+ def _cascade_env_map(
55
55
  env_map: Mapping[str, str],
56
56
  other_env_map: Mapping[str, str]
57
57
  ) -> Mapping[str, str]:
@@ -63,14 +63,14 @@ def cascade_env_map(
63
63
 
64
64
 
65
65
  @typechecked
66
- def get_env_name(env: Env) -> str:
66
+ def _get_env_name(env: Env) -> str:
67
67
  if env.os_name is None:
68
68
  return env.name
69
69
  return env.os_name
70
70
 
71
71
 
72
72
  @typechecked
73
- def get_env_default(env: Env) -> str:
73
+ def _get_env_default(env: Env) -> str:
74
74
  if is_probably_jinja(env.default):
75
75
  return ''
76
76
  return env.default
@@ -2,11 +2,11 @@ set -e
2
2
  auth_rsync(){
3
3
  if [ "$_CONFIG_SSH_KEY" != "" ]
4
4
  then
5
- rsync -avz -e "ssh -i $_CONFIG_SSH_KEY -p $_CONFIG_PORT" $@
5
+ rsync --mkpath -avz -e "ssh -i $_CONFIG_SSH_KEY -p $_CONFIG_PORT" $@
6
6
  elif [ "$_CONFIG_PASSWORD" != "" ]
7
7
  then
8
- sshpass -p "$_CONFIG_PASSWORD" rsync -avz -e "ssh -p $_CONFIG_PORT" $@
8
+ sshpass -p "$_CONFIG_PASSWORD" rsync --mkpath -avz -e "ssh -p $_CONFIG_PORT" $@
9
9
  else
10
- rsync -avz -e "ssh -p $_CONFIG_PORT" $@
10
+ rsync --mkpath -avz -e "ssh -p $_CONFIG_PORT" $@
11
11
  fi
12
12
  }
zrb/task/any_task.py CHANGED
@@ -65,19 +65,35 @@ class AnyTask(ABC):
65
65
  pass
66
66
 
67
67
  @abstractmethod
68
- def add_upstreams(self, *upstreams: TAnyTask):
68
+ def add_upstream(self, *upstreams: TAnyTask):
69
69
  pass
70
70
 
71
71
  @abstractmethod
72
- def add_inputs(self, *inputs: AnyInput):
72
+ def insert_input(self, *inputs: AnyInput):
73
73
  pass
74
74
 
75
75
  @abstractmethod
76
- def add_envs(self, *envs: Env):
76
+ def add_input(self, *inputs: AnyInput):
77
77
  pass
78
78
 
79
79
  @abstractmethod
80
- def add_env_files(self, *env_files: EnvFile):
80
+ def insert_env(self, *envs: Env):
81
+ pass
82
+
83
+ @abstractmethod
84
+ def add_env(self, *envs: Env):
85
+ pass
86
+
87
+ @abstractmethod
88
+ def insert_env_file(self, *env_files: EnvFile):
89
+ pass
90
+
91
+ @abstractmethod
92
+ def add_env_file(self, *env_files: EnvFile):
93
+ pass
94
+
95
+ @abstractmethod
96
+ def _set_execution_id(self, execution_id: str):
81
97
  pass
82
98
 
83
99
  @abstractmethod
@@ -114,6 +130,10 @@ class AnyTask(ABC):
114
130
  def set_checking_interval(self, new_checking_interval: Union[float, int]):
115
131
  pass
116
132
 
133
+ @abstractmethod
134
+ def get_execution_id(self) -> str:
135
+ pass
136
+
117
137
  @abstractmethod
118
138
  def get_icon(self) -> str:
119
139
  pass
@@ -131,31 +151,51 @@ class AnyTask(ABC):
131
151
  pass
132
152
 
133
153
  @abstractmethod
134
- def get_complete_cmd_name(self) -> str:
154
+ def get_full_cmd_name(self) -> str:
155
+ pass
156
+
157
+ @abstractmethod
158
+ def inject_env_files(self):
135
159
  pass
136
160
 
137
161
  @abstractmethod
138
- def get_env_files(self) -> List[EnvFile]:
162
+ def _get_env_files(self) -> List[EnvFile]:
139
163
  pass
140
164
 
141
165
  @abstractmethod
142
- def get_envs(self) -> List[Env]:
166
+ def inject_envs(self):
143
167
  pass
144
168
 
145
169
  @abstractmethod
146
- def get_inputs(self) -> List[AnyInput]:
170
+ def _get_envs(self) -> List[Env]:
147
171
  pass
148
172
 
149
173
  @abstractmethod
150
- def get_checkers(self) -> Iterable[TAnyTask]:
174
+ def inject_inputs(self):
151
175
  pass
152
176
 
153
177
  @abstractmethod
154
- def get_upstreams(self) -> Iterable[TAnyTask]:
178
+ def _get_inputs(self) -> List[AnyInput]:
155
179
  pass
156
180
 
157
181
  @abstractmethod
158
- def get_all_inputs(self) -> Iterable[AnyInput]:
182
+ def inject_checkers(self):
183
+ pass
184
+
185
+ @abstractmethod
186
+ def _get_checkers(self) -> Iterable[TAnyTask]:
187
+ pass
188
+
189
+ @abstractmethod
190
+ def inject_upstreams(self):
191
+ pass
192
+
193
+ @abstractmethod
194
+ def _get_upstreams(self) -> Iterable[TAnyTask]:
195
+ pass
196
+
197
+ @abstractmethod
198
+ def _get_combined_inputs(self) -> Iterable[AnyInput]:
159
199
  pass
160
200
 
161
201
  @abstractmethod
@@ -198,12 +238,6 @@ class AnyTask(ABC):
198
238
  def get_env_map(self) -> Mapping[str, Any]:
199
239
  pass
200
240
 
201
- @abstractmethod
202
- def inject_env_map(
203
- self, env_map: Mapping[str, str], override: bool = False
204
- ):
205
- pass
206
-
207
241
  @abstractmethod
208
242
  def render_any(
209
243
  self, val: Any, data: Optional[Mapping[str, Any]] = None
@@ -2,6 +2,7 @@ from zrb.helper.typing import (
2
2
  Any, Callable, Iterable, Mapping, Optional, Union, TypeVar
3
3
  )
4
4
  from zrb.helper.typecheck import typechecked
5
+ from zrb.helper.util import to_snake_case
5
6
  from zrb.task.any_task import AnyTask
6
7
  from zrb.task.any_task_event_handler import (
7
8
  OnTriggered, OnWaiting, OnSkipped, OnStarted, OnReady, OnRetry, OnFailed
@@ -34,13 +35,15 @@ class RemoteConfig:
34
35
  user: str = '',
35
36
  password: str = '',
36
37
  ssh_key: str = '',
37
- port: int = 22
38
+ port: int = 22,
39
+ config_map: Optional[Mapping[str, str]] = None
38
40
  ):
39
41
  self.host = host
40
42
  self.user = user
41
43
  self.password = password
42
44
  self.ssh_key = ssh_key
43
45
  self.port = port
46
+ self.config_map = {} if config_map is None else config_map
44
47
 
45
48
 
46
49
  @typechecked
@@ -123,28 +126,50 @@ class SingleBaseRemoteCmdTask(CmdTask):
123
126
  def copy(self) -> TSingleBaseRemoteCmdTask:
124
127
  return copy.deepcopy(self)
125
128
 
126
- def _get_shell_env_map(self) -> Mapping[str, Any]:
127
- env_map = super()._get_shell_env_map()
128
- env_map['_CONFIG_HOST'] = self.render_str(self._remote_config.host)
129
- env_map['_CONFIG_PORT'] = str(self.render_int(
130
- self._remote_config.port)
131
- )
132
- env_map['_CONFIG_SSH_KEY'] = self.render_str(
133
- self._remote_config.ssh_key
134
- )
135
- env_map['_CONFIG_USER'] = self.render_str(self._remote_config.user)
136
- env_map['_CONFIG_PASSWORD'] = self.render_str(
137
- self._remote_config.password
129
+ def inject_envs(self):
130
+ super().inject_envs()
131
+ # add remote config properties as env
132
+ self.add_env(
133
+ Env(
134
+ name='_CONFIG_HOST', os_name='',
135
+ default=self.render_str(self._remote_config.host)
136
+ ),
137
+ Env(
138
+ name='_CONFIG_PORT', os_name='',
139
+ default=str(self.render_int(self._remote_config.port))
140
+ ),
141
+ Env(
142
+ name='_CONFIG_SSH_KEY', os_name='',
143
+ default=self.render_str(self._remote_config.ssh_key)
144
+ ),
145
+ Env(
146
+ name='_CONFIG_USER', os_name='',
147
+ default=self.render_str(self._remote_config.user)
148
+ ),
149
+ Env(
150
+ name='_CONFIG_PASSWORD', os_name='',
151
+ default=self.render_str(self._remote_config.password)
152
+ ),
138
153
  )
139
- return env_map
154
+ for key, val in self._remote_config.config_map.items():
155
+ upper_snake_key = to_snake_case(key).upper()
156
+ rendered_val = self.render_str(val)
157
+ # add remote config map as env
158
+ self.add_env(
159
+ Env(
160
+ name='_CONFIG_MAP_' + upper_snake_key,
161
+ os_name='',
162
+ default=rendered_val
163
+ )
164
+ )
140
165
 
141
- def _get_cmd_str(self, *args: Any, **kwargs: Any) -> str:
166
+ def get_cmd_script(self, *args: Any, **kwargs: Any) -> str:
142
167
  cmd_str = '\n'.join([
143
- self._create_cmd_str(
168
+ self._create_cmd_script(
144
169
  self._pre_cmd_path, self._pre_cmd, *args, **kwargs
145
170
  ),
146
- super()._get_cmd_str(*args, **kwargs),
147
- self._create_cmd_str(
171
+ super().get_cmd_script(*args, **kwargs),
172
+ self._create_cmd_script(
148
173
  self._post_cmd_path, self._post_cmd, *args, **kwargs
149
174
  ),
150
175
  ])
zrb/task/base_task.py CHANGED
@@ -12,11 +12,13 @@ from zrb.task.base_task_composite import (
12
12
  )
13
13
  from zrb.advertisement import advertisements
14
14
  from zrb.task_group.group import Group
15
+ from zrb.task_env.constant import RESERVED_ENV_NAMES
15
16
  from zrb.task_env.env import Env
16
17
  from zrb.task_env.env_file import EnvFile
17
18
  from zrb.task_input.any_input import AnyInput
18
19
  from zrb.task_input.constant import RESERVED_INPUT_NAMES
19
20
  from zrb.helper.accessories.color import colored
21
+ from zrb.helper.accessories.name import get_random_name
20
22
  from zrb.helper.advertisement import get_advertisement
21
23
  from zrb.helper.string.modification import double_quote
22
24
  from zrb.helper.string.conversion import to_variable_name
@@ -108,30 +110,28 @@ class BaseTask(
108
110
  self._is_execution_started: bool = False
109
111
  self._args: List[Any] = []
110
112
  self._kwargs: Mapping[str, Any] = {}
111
- self._allow_add_upstreams: bool = True
112
113
 
113
114
  def copy(self) -> AnyTask:
114
115
  return copy.deepcopy(self)
115
116
 
116
- def get_all_inputs(self) -> Iterable[AnyInput]:
117
+ def _get_combined_inputs(self) -> Iterable[AnyInput]:
117
118
  ''''
118
119
  Getting all inputs of this task and all its upstream, non-duplicated.
119
120
  '''
120
121
  if self._all_inputs is not None:
121
122
  return self._all_inputs
122
- self._allow_add_upstreams = False
123
- self._allow_add_inputs = False
124
123
  self._all_inputs: List[AnyInput] = []
125
124
  existing_input_names: Mapping[str, bool] = {}
126
125
  # Add task inputs
127
- for input_index, first_occurence_task_input in enumerate(self._inputs):
126
+ inputs = self._get_inputs()
127
+ for input_index, first_occurence_task_input in enumerate(inputs):
128
128
  input_name = first_occurence_task_input.get_name()
129
129
  if input_name in existing_input_names:
130
130
  continue
131
131
  # Look for all input with the same name in the current task
132
132
  task_inputs = [
133
133
  candidate
134
- for candidate in self._inputs[input_index:]
134
+ for candidate in inputs[input_index:]
135
135
  if candidate.get_name() == input_name
136
136
  ]
137
137
  # Get the last input, and add it to _all_inputs
@@ -139,13 +139,15 @@ class BaseTask(
139
139
  self._all_inputs.append(task_input)
140
140
  existing_input_names[input_name] = True
141
141
  # Add upstream inputs
142
- for upstream in self._upstreams:
143
- upstream_inputs = upstream.get_all_inputs()
142
+ for upstream in self._get_upstreams():
143
+ upstream_inputs = upstream._get_combined_inputs()
144
144
  for upstream_input in upstream_inputs:
145
145
  if upstream_input.get_name() in existing_input_names:
146
146
  continue
147
147
  self._all_inputs.append(upstream_input)
148
148
  existing_input_names[upstream_input.get_name()] = True
149
+ self._allow_add_upstreams = False
150
+ self._allow_add_inputs = False
149
151
  return self._all_inputs
150
152
 
151
153
  def to_function(
@@ -165,21 +167,6 @@ class BaseTask(
165
167
  ))
166
168
  return function
167
169
 
168
- def add_upstreams(self, *upstreams: AnyTask):
169
- if not self._allow_add_upstreams:
170
- raise Exception(f'Cannot add upstreams on `{self._name}`')
171
- self._upstreams += upstreams
172
-
173
- def inject_env_map(
174
- self, env_map: Mapping[str, str], override: bool = False
175
- ):
176
- '''
177
- Set new values for current task's env map
178
- '''
179
- for key, val in env_map.items():
180
- if override or key not in self.get_env_map():
181
- self._set_env_map(key, val)
182
-
183
170
  async def run(self, *args: Any, **kwargs: Any) -> Any:
184
171
  '''
185
172
  Do task execution
@@ -257,38 +244,42 @@ class BaseTask(
257
244
  return True
258
245
  self._is_keyval_set = True
259
246
  self.log_info('Set input map')
260
- for task_input in self.get_all_inputs():
247
+ for task_input in self._get_combined_inputs():
261
248
  input_name = self._get_normalized_input_key(task_input.get_name())
262
249
  input_value = self.render_any(
263
250
  kwargs.get(input_name, task_input.get_default())
264
251
  )
265
252
  self._set_input_map(input_name, input_value)
253
+ self._set_input_map('_execution_id', self._execution_id)
266
254
  self.log_debug(
267
255
  'Input map:\n' + map_to_str(self.get_input_map(), item_prefix=' ')
268
256
  )
269
257
  self.log_info('Merging task envs, task env files, and native envs')
270
- for env_name, env in self._get_all_envs().items():
258
+ for env_name, env in self._get_combined_env().items():
271
259
  env_value = env.get(env_prefix)
272
260
  if env.renderable:
273
261
  env_value = self.render_any(env_value)
274
262
  self._set_env_map(env_name, env_value)
263
+ self._set_env_map('_ZRB_EXECUTION_ID', self._execution_id)
275
264
  self.log_debug(
276
265
  'Env map:\n' + map_to_str(self.get_env_map(), item_prefix=' ')
277
266
  )
278
267
 
279
- def _get_all_envs(self) -> Mapping[str, Env]:
280
- self._allow_add_envs = False
281
- self._allow_add_env_files = False
268
+ def _get_combined_env(self) -> Mapping[str, Env]:
282
269
  all_envs: Mapping[str, Env] = {}
283
270
  for env_name in os.environ:
271
+ if env_name in RESERVED_ENV_NAMES:
272
+ continue
284
273
  all_envs[env_name] = Env(
285
274
  name=env_name, os_name=env_name, renderable=False
286
275
  )
287
- for env_file in self._env_files:
276
+ for env_file in self._get_env_files():
288
277
  for env in env_file.get_envs():
289
278
  all_envs[env.name] = env
290
- for env in self._envs:
279
+ for env in self._get_envs():
291
280
  all_envs[env.name] = env
281
+ self._allow_add_envs = False
282
+ self._allow_add_env_files = False
292
283
  return all_envs
293
284
 
294
285
  def _get_normalized_input_key(self, key: str) -> str:
@@ -305,6 +296,10 @@ class BaseTask(
305
296
  ):
306
297
  try:
307
298
  self._start_timer()
299
+ if self.get_execution_id() == '':
300
+ self._set_execution_id(
301
+ get_random_name(add_random_digit=True, digit_count=5)
302
+ )
308
303
  self.log_info('Set input and env map')
309
304
  await self._set_keyval(kwargs=kwargs, env_prefix=env_prefix)
310
305
  self.log_info('Set run kwargs')
@@ -342,9 +337,10 @@ class BaseTask(
342
337
  return
343
338
  if self._return_upstream_result:
344
339
  # if _return_upstream_result, result is list (see: self._run_all)
340
+ upstreams = self._get_upstreams()
345
341
  upstream_results = list(result)
346
342
  for upstream_index, upstream_result in enumerate(upstream_results):
347
- self._upstreams[upstream_index]._print_result(upstream_result)
343
+ upstreams[upstream_index]._print_result(upstream_result)
348
344
  return
349
345
  self.print_result(result)
350
346
 
@@ -382,14 +378,14 @@ class BaseTask(
382
378
 
383
379
  def _show_run_command(self):
384
380
  params: List[str] = [double_quote(arg) for arg in self._args]
385
- for task_input in self.get_all_inputs():
381
+ for task_input in self._get_combined_inputs():
386
382
  if task_input.is_hidden():
387
383
  continue
388
384
  key = task_input.get_name()
389
385
  kwarg_key = self._get_normalized_input_key(key)
390
386
  quoted_value = double_quote(str(self._kwargs[kwarg_key]))
391
387
  params.append(f'--{key} {quoted_value}')
392
- run_cmd = self.get_complete_cmd_name()
388
+ run_cmd = self.get_full_cmd_name()
393
389
  run_cmd_with_param = run_cmd
394
390
  if len(params) > 0:
395
391
  param_str = ' '.join(params)
@@ -426,6 +422,7 @@ class BaseTask(
426
422
  await asyncio.sleep(0.1)
427
423
  check_coroutines: Iterable[asyncio.Task] = []
428
424
  for checker_task in self._checkers:
425
+ checker_task._set_execution_id(self.get_execution_id())
429
426
  check_coroutines.append(
430
427
  asyncio.create_task(checker_task._run_all())
431
428
  )
@@ -437,7 +434,8 @@ class BaseTask(
437
434
  coroutines: Iterable[asyncio.Task] = []
438
435
  # Add upstream tasks to processes
439
436
  self._allow_add_upstreams = False
440
- for upstream_task in self._upstreams:
437
+ for upstream_task in self._get_upstreams():
438
+ upstream_task._set_execution_id(self.get_execution_id())
441
439
  coroutines.append(asyncio.create_task(
442
440
  upstream_task._run_all(**kwargs)
443
441
  ))
@@ -459,7 +457,7 @@ class BaseTask(
459
457
  # get upstream checker
460
458
  upstream_check_processes: Iterable[asyncio.Task] = []
461
459
  self._allow_add_upstreams = False
462
- for upstream_task in self._upstreams:
460
+ for upstream_task in self._get_upstreams():
463
461
  upstream_check_processes.append(asyncio.create_task(
464
462
  upstream_task._loop_check()
465
463
  ))
@@ -503,7 +501,7 @@ class BaseTask(
503
501
 
504
502
  async def _set_keyval(self, kwargs: Mapping[str, Any], env_prefix: str):
505
503
  # if input is not in input_map, add default values
506
- for task_input in self.get_all_inputs():
504
+ for task_input in self._get_combined_inputs():
507
505
  key = self._get_normalized_input_key(task_input.get_name())
508
506
  if key in kwargs:
509
507
  continue
@@ -514,20 +512,20 @@ class BaseTask(
514
512
  new_kwargs = copy.deepcopy(kwargs)
515
513
  new_kwargs.update(self.get_input_map())
516
514
  upstream_coroutines = []
517
- # set uplstreams keyval
515
+ # set upstreams keyval
518
516
  self._allow_add_upstreams = False
519
- for upstream_task in self._upstreams:
517
+ for upstream_task in self._get_upstreams():
520
518
  upstream_coroutines.append(asyncio.create_task(
521
519
  upstream_task._set_keyval(
522
520
  kwargs=new_kwargs, env_prefix=env_prefix
523
521
  )
524
522
  ))
525
523
  # set checker keyval
526
- local_env_map = self.get_env_map()
524
+ # local_env_map = self.get_env_map()
527
525
  checker_coroutines = []
528
526
  for checker_task in self._checkers:
529
- checker_task._inputs += self._inputs
530
- checker_task.inject_env_map(local_env_map, override=True)
527
+ checker_task.add_input(*self._get_inputs())
528
+ checker_task.add_env(*self._get_envs())
531
529
  checker_coroutines.append(asyncio.create_task(
532
530
  checker_task._set_keyval(
533
531
  kwargs=new_kwargs, env_prefix=env_prefix
@@ -66,6 +66,17 @@ class CommonTaskModel():
66
66
  self._allow_add_envs = True
67
67
  self._allow_add_env_files = True
68
68
  self._allow_add_inputs = True
69
+ self._allow_add_upstreams: bool = True
70
+ self._has_already_inject_env_files: bool = False
71
+ self._has_already_inject_envs: bool = False
72
+ self._has_already_inject_inputs: bool = False
73
+ self._has_already_inject_checkers: bool = False
74
+ self._has_already_inject_upstreams: bool = False
75
+ self._execution_id = ''
76
+
77
+ def _set_execution_id(self, execution_id: str):
78
+ if self._execution_id != '':
79
+ self._execution_id = execution_id
69
80
 
70
81
  def set_name(self, new_name: str):
71
82
  if self._description == self._name:
@@ -95,20 +106,48 @@ class CommonTaskModel():
95
106
  def set_checking_interval(self, new_checking_interval: Union[float, int]):
96
107
  self._checking_interval = new_checking_interval
97
108
 
98
- def add_inputs(self, *inputs: AnyInput):
109
+ def insert_input(self, *inputs: AnyInput):
99
110
  if not self._allow_add_inputs:
100
- raise Exception(f'Cannot add inputs on `{self._name}`')
101
- self._inputs += inputs
111
+ raise Exception(f'Cannot insert inputs for `{self._name}`')
112
+ self._inputs = list(inputs) + list(self._inputs)
102
113
 
103
- def add_envs(self, *envs: Env):
114
+ def add_input(self, *inputs: AnyInput):
115
+ if not self._allow_add_inputs:
116
+ raise Exception(f'Cannot add inputs for `{self._name}`')
117
+ self._inputs = list(self._inputs) + list(inputs)
118
+
119
+ def insert_env(self, *envs: Env):
120
+ if not self._allow_add_envs:
121
+ raise Exception(f'Cannot insert envs to `{self._name}`')
122
+ self._envs = list(envs) + list(self._envs)
123
+
124
+ def add_env(self, *envs: Env):
104
125
  if not self._allow_add_envs:
105
- raise Exception(f'Cannot add envs on `{self._name}`')
106
- self._envs += envs
126
+ raise Exception(f'Cannot add envs to `{self._name}`')
127
+ self._envs = list(self._envs) + list(envs)
107
128
 
108
- def add_env_files(self, *env_files: EnvFile):
129
+ def insert_env_file(self, *env_files: EnvFile):
109
130
  if not self._allow_add_env_files:
110
- raise Exception(f'Cannot add env_files on `{self._name}`')
111
- self._env_files += env_files
131
+ raise Exception(f'Cannot insert env_files to `{self._name}`')
132
+ self._env_files = list(env_files) + list(self._env_files)
133
+
134
+ def add_env_file(self, *env_files: EnvFile):
135
+ if not self._allow_add_env_files:
136
+ raise Exception(f'Cannot add env_files to `{self._name}`')
137
+ self._env_files = list(self._env_files) + list(env_files)
138
+
139
+ def insert_upstream(self, *upstreams: AnyTask):
140
+ if not self._allow_add_upstreams:
141
+ raise Exception(f'Cannot insert upstreams to `{self._name}`')
142
+ self._upstreams = list(upstreams) + list(self._upstreams)
143
+
144
+ def add_upstream(self, *upstreams: AnyTask):
145
+ if not self._allow_add_upstreams:
146
+ raise Exception(f'Cannot add upstreams to `{self._name}`')
147
+ self._upstreams = list(self._upstreams) + list(upstreams)
148
+
149
+ def get_execution_id(self) -> str:
150
+ return self._execution_id
112
151
 
113
152
  def get_icon(self) -> str:
114
153
  return self._icon
@@ -116,20 +155,50 @@ class CommonTaskModel():
116
155
  def get_color(self) -> str:
117
156
  return self._color
118
157
 
119
- def get_env_files(self) -> List[EnvFile]:
158
+ def inject_env_files(self):
159
+ pass
160
+
161
+ def _get_env_files(self) -> List[EnvFile]:
162
+ if not self._has_already_inject_env_files:
163
+ self.inject_env_files()
164
+ self._has_already_inject_env_files = True
120
165
  return self._env_files
121
166
 
122
- def get_envs(self) -> List[Env]:
123
- return self._envs
167
+ def inject_envs(self):
168
+ pass
169
+
170
+ def _get_envs(self) -> List[Env]:
171
+ if not self._has_already_inject_envs:
172
+ self.inject_envs()
173
+ self._has_already_inject_envs = True
174
+ return list(self._envs)
175
+
176
+ def inject_inputs(self):
177
+ pass
178
+
179
+ def _get_inputs(self) -> List[AnyInput]:
180
+ if not self._has_already_inject_inputs:
181
+ self.inject_inputs()
182
+ self._has_already_inject_inputs = True
183
+ return list(self._inputs)
184
+
185
+ def inject_checkers(self):
186
+ pass
124
187
 
125
- def get_inputs(self) -> List[AnyInput]:
126
- return self._inputs
188
+ def _get_checkers(self) -> List[AnyTask]:
189
+ if not self._has_already_inject_checkers:
190
+ self.inject_checkers()
191
+ self._has_already_inject_checkers = True
192
+ return list(self._checkers)
127
193
 
128
- def get_checkers(self) -> Iterable[AnyTask]:
129
- return self._checkers
194
+ def inject_upstreams(self):
195
+ pass
130
196
 
131
- def get_upstreams(self) -> Iterable[AnyTask]:
132
- return self._upstreams
197
+ def _get_upstreams(self) -> List[AnyTask]:
198
+ if not self._has_already_inject_upstreams:
199
+ self.inject_upstreams()
200
+ self._has_already_inject_upstreams = True
201
+ return list(self._upstreams)
133
202
 
134
203
  def get_description(self) -> str:
135
204
  return self._description
@@ -354,7 +423,7 @@ class TaskModelWithPrinterAndTracker(
354
423
  run: Optional[Callable[..., Any]] = None,
355
424
  should_execute: Union[bool, str, Callable[..., bool]] = True
356
425
  ):
357
- self._filled_complete_name: Optional[str] = None
426
+ self._rjust_full_cmd_name: Optional[str] = None
358
427
  self._has_cli_interface = False
359
428
  self._complete_name: Optional[str] = None
360
429
  CommonTaskModel.__init__(
@@ -441,13 +510,13 @@ class TaskModelWithPrinterAndTracker(
441
510
  def _get_print_prefix(self) -> str:
442
511
  common_prefix = self._get_common_prefix(show_time=show_time)
443
512
  icon = self.get_icon()
444
- truncated_name = self._get_filled_complete_name()
513
+ truncated_name = self._get_rjust_full_cmd_name()
445
514
  return f'{common_prefix} {icon} {truncated_name}'
446
515
 
447
516
  def _get_log_prefix(self) -> str:
448
517
  common_prefix = self._get_common_prefix(show_time=False)
449
518
  icon = self.get_icon()
450
- filled_name = self._get_filled_complete_name()
519
+ filled_name = self._get_rjust_full_cmd_name()
451
520
  return f'{common_prefix} {icon} {filled_name}'
452
521
 
453
522
  def _get_common_prefix(self, show_time: bool) -> str:
@@ -459,14 +528,14 @@ class TaskModelWithPrinterAndTracker(
459
528
  return f'◷ {now} ❁ {pid} → {attempt}/{max_attempt}'
460
529
  return f'❁ {pid} → {attempt}/{max_attempt}'
461
530
 
462
- def _get_filled_complete_name(self) -> str:
463
- if self._filled_complete_name is not None:
464
- return self._filled_complete_name
465
- complete_name = self.get_complete_cmd_name()
466
- self._filled_complete_name = complete_name.rjust(LOG_NAME_LENGTH, ' ')
467
- return self._filled_complete_name
531
+ def _get_rjust_full_cmd_name(self) -> str:
532
+ if self._rjust_full_cmd_name is not None:
533
+ return self._rjust_full_cmd_name
534
+ complete_name = self.get_full_cmd_name()
535
+ self._rjust_full_cmd_name = complete_name.rjust(LOG_NAME_LENGTH, ' ')
536
+ return self._rjust_full_cmd_name
468
537
 
469
- def get_complete_cmd_name(self) -> str:
538
+ def get_full_cmd_name(self) -> str:
470
539
  if self._complete_name is not None:
471
540
  return self._complete_name
472
541
  executable_prefix = ''
zrb/task/cmd_task.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from zrb.helper.typing import (
2
- Any, Callable, Iterable, List, Mapping, Optional, Union, TypeVar
2
+ Any, Callable, Iterable, List, Optional, Union, TypeVar
3
3
  )
4
4
  from zrb.helper.typecheck import typechecked
5
5
  from zrb.task.any_task import AnyTask
@@ -182,18 +182,17 @@ class CmdTask(BaseTask):
182
182
  return
183
183
  print(result.output)
184
184
 
185
- def _get_shell_env_map(self) -> Mapping[str, Any]:
186
- env_map = self.get_env_map()
185
+ def inject_envs(self):
186
+ super().inject_envs()
187
187
  input_map = self.get_input_map()
188
188
  for input_name, input_value in input_map.items():
189
- upper_input_name = '_INPUT_' + input_name.upper()
190
- if upper_input_name not in env_map:
191
- env_map[upper_input_name] = f'{input_value}'
192
- return env_map
189
+ env_name = '_INPUT_' + input_name.upper()
190
+ self.add_env(
191
+ Env(name=env_name, os_name='', default=str(input_value))
192
+ )
193
193
 
194
194
  async def run(self, *args: Any, **kwargs: Any) -> CmdResult:
195
- cmd = self._get_cmd_str(*args, **kwargs)
196
- env_map = self._get_shell_env_map()
195
+ cmd = self.get_cmd_script(*args, **kwargs)
197
196
  self.print_out_dark('Run script: ' + self._get_multiline_repr(cmd))
198
197
  self.print_out_dark('Working directory: ' + self._cwd)
199
198
  self._output_buffer = []
@@ -203,7 +202,7 @@ class CmdTask(BaseTask):
203
202
  cwd=self._cwd,
204
203
  stdout=asyncio.subprocess.PIPE,
205
204
  stderr=asyncio.subprocess.PIPE,
206
- env=env_map,
205
+ env=self.get_env_map(),
207
206
  shell=True,
208
207
  executable=self._executable,
209
208
  close_fds=True,
@@ -314,21 +313,25 @@ class CmdTask(BaseTask):
314
313
  await stdout_log_process
315
314
  await stderr_log_process
316
315
 
317
- def _get_cmd_str(self, *args: Any, **kwargs: Any) -> str:
318
- return self._create_cmd_str(self._cmd_path, self._cmd, *args, **kwargs)
316
+ def get_cmd_script(self, *args: Any, **kwargs: Any) -> str:
317
+ return self._create_cmd_script(
318
+ self._cmd_path, self._cmd, *args, **kwargs
319
+ )
319
320
 
320
- def _create_cmd_str(
321
+ def _create_cmd_script(
321
322
  self, cmd_path: CmdVal, cmd: CmdVal, *args: Any, **kwargs: Any
322
323
  ) -> str:
323
324
  if not isinstance(cmd_path, str) or cmd_path != '':
324
325
  if callable(cmd_path):
325
- return self._render_cmd_path_str(cmd_path(*args, **kwargs))
326
- return self._render_cmd_path_str(cmd_path)
326
+ return self._get_rendered_cmd_path(cmd_path(*args, **kwargs))
327
+ return self._get_rendered_cmd_path(cmd_path)
327
328
  if callable(cmd):
328
- return self._render_cmd_str(cmd(*args, **kwargs))
329
- return self._render_cmd_str(cmd)
329
+ return self._get_rendered_cmd(cmd(*args, **kwargs))
330
+ return self._get_rendered_cmd(cmd)
330
331
 
331
- def _render_cmd_path_str(self, cmd_path: Union[str, Iterable[str]]) -> str:
332
+ def _get_rendered_cmd_path(
333
+ self, cmd_path: Union[str, Iterable[str]]
334
+ ) -> str:
332
335
  if isinstance(cmd_path, str):
333
336
  return self.render_file(cmd_path)
334
337
  return '\n'.join([
@@ -336,7 +339,7 @@ class CmdTask(BaseTask):
336
339
  for cmd_path_str in cmd_path
337
340
  ])
338
341
 
339
- def _render_cmd_str(self, cmd: Union[str, Iterable[str]]) -> str:
342
+ def _get_rendered_cmd(self, cmd: Union[str, Iterable[str]]) -> str:
340
343
  if isinstance(cmd, str):
341
344
  return self.render_str(cmd)
342
345
  return self.render_str('\n'.join(list(cmd)))
@@ -7,6 +7,7 @@ from zrb.task.any_task import AnyTask
7
7
  from zrb.task.any_task_event_handler import (
8
8
  OnTriggered, OnWaiting, OnSkipped, OnStarted, OnReady, OnRetry, OnFailed
9
9
  )
10
+ from zrb.task_env.constant import RESERVED_ENV_NAMES
10
11
  from zrb.task_env.env import Env
11
12
  from zrb.task_env.env_file import EnvFile
12
13
  from zrb.task_group.group import Group
@@ -152,7 +153,8 @@ class DockerComposeTask(CmdTask):
152
153
  )
153
154
  # Flag to make mark whether service config and compose environments
154
155
  # has been added to this task's envs and env_files
155
- self._is_additional_env_added = False
156
+ self._is_compose_additional_env_added = False
157
+ self._is_compose_additional_env_file_added = False
156
158
 
157
159
  def copy(self) -> TDockerComposeTask:
158
160
  return super().copy()
@@ -165,45 +167,30 @@ class DockerComposeTask(CmdTask):
165
167
  os.remove(self._compose_runtime_file)
166
168
  return result
167
169
 
168
- def _get_all_envs(self) -> Mapping[str, Env]:
169
- '''
170
- This method override BaseTask's _get_all_envs.
171
- Whenever _get_all_envs is called, we want to make sure that:
172
- - Service config's envs and env_files are included
173
- - Any environment defined in docker compose file is also included
174
- '''
175
- if self._is_additional_env_added:
176
- return super()._get_all_envs()
177
- self._is_additional_env_added = True
178
- # define additional envs and additonal env_files
179
- additional_envs: List[Env] = []
180
- additional_env_files: List[EnvFile] = []
181
- # populate additional envs and additional env_files
182
- # with service configs
170
+ def inject_envs(self):
171
+ super().inject_envs()
172
+ # inject envs from service_configs
183
173
  for _, service_config in self._compose_service_configs.items():
184
- additional_env_files += service_config.get_env_files()
185
- additional_envs += service_config.get_envs()
186
- # populate additional envs and additional env_files with
187
- # compose envs
188
- data = read_compose_file(self._compose_template_file)
189
- env_map = fetch_compose_file_env_map(data)
190
- registered_env_map: Mapping[str, bool] = {}
174
+ self.insert_env(*service_config.get_envs())
175
+ # inject envs from docker compose file
176
+ compose_data = read_compose_file(self._compose_template_file)
177
+ env_map = fetch_compose_file_env_map(compose_data)
178
+ added_env_map: Mapping[str, bool] = {}
191
179
  for key, value in env_map.items():
192
180
  # Need to get this everytime because we only want
193
181
  # the first compose file env value for a certain key
194
- if key in registered_env_map:
182
+ if key in RESERVED_ENV_NAMES or key in added_env_map:
195
183
  continue
184
+ added_env_map[key] = True
196
185
  os_name = key
197
186
  if self._compose_env_prefix != '':
198
187
  os_name = f'{self._compose_env_prefix}_{os_name}'
199
- compose_env = Env(name=key, os_name=os_name, default=value)
200
- additional_envs.append(compose_env)
201
- registered_env_map[key] = True
202
- # Add additional envs and addition env files to this task
203
- self._envs = additional_envs + list(self._envs)
204
- self._env_files = additional_env_files + list(self._env_files)
205
- # get all envs
206
- return super()._get_all_envs()
188
+ self.insert_env(Env(name=key, os_name=os_name, default=value))
189
+
190
+ def inject_env_files(self):
191
+ # inject env_files from service_configs
192
+ for _, service_config in self._compose_service_configs.items():
193
+ self.insert_env_file(*service_config.get_env_files())
207
194
 
208
195
  def _generate_compose_runtime_file(self):
209
196
  compose_data = read_compose_file(self._compose_template_file)
@@ -310,8 +297,8 @@ class DockerComposeTask(CmdTask):
310
297
  return os.path.join(self._cwd, compose_file)
311
298
  raise Exception(f'Invalid compose file: {compose_file}')
312
299
 
313
- def _get_cmd_str(self, *args: Any, **kwargs: Any) -> str:
314
- setup_cmd_str = self._create_cmd_str(
300
+ def get_cmd_script(self, *args: Any, **kwargs: Any) -> str:
301
+ setup_cmd_str = self._create_cmd_script(
315
302
  self._setup_cmd_path, self._setup_cmd, *args, **kwargs
316
303
  )
317
304
  command_options = dict(self._compose_options)
zrb/task/flow_task.py CHANGED
@@ -107,10 +107,10 @@ class FlowTask(BaseTask):
107
107
  embeded_tasks: List[AnyTask] = []
108
108
  for task in tasks:
109
109
  embeded_task = task.copy()
110
- embeded_task.add_upstreams(*upstreams)
111
- embeded_task.add_envs(*envs)
112
- embeded_task.add_env_files(*env_files)
113
- embeded_task.add_inputs(*inputs)
110
+ embeded_task.add_upstream(*upstreams)
111
+ embeded_task.add_env(*envs)
112
+ embeded_task.add_env_file(*env_files)
113
+ embeded_task.add_input(*inputs)
114
114
  embeded_tasks.append(embeded_task)
115
115
  return embeded_tasks
116
116
 
@@ -0,0 +1 @@
1
+ RESERVED_ENV_NAMES = ('_ZRB_EXECUTION_ID',)
zrb/task_env/env.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from zrb.helper.typing import Optional
2
2
  from zrb.helper.typecheck import typechecked
3
+ from zrb.task_env.constant import RESERVED_ENV_NAMES
3
4
  import os
4
5
 
5
6
 
@@ -16,6 +17,8 @@ class Env():
16
17
  default: str = '',
17
18
  renderable: bool = True,
18
19
  ):
20
+ if name in RESERVED_ENV_NAMES:
21
+ raise ValueError(f'Forbidden input name: {name}')
19
22
  self.name: str = name
20
23
  self.os_name: str = os_name if os_name is not None else name
21
24
  self.default: str = default
zrb/task_env/env_file.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from zrb.helper.typing import List, Optional
2
2
  from zrb.helper.typecheck import typechecked
3
3
  from dotenv import dotenv_values
4
+ from zrb.task_env.constant import RESERVED_ENV_NAMES
4
5
  from zrb.task_env.env import Env
5
6
 
6
7
 
@@ -25,6 +26,8 @@ class EnvFile():
25
26
  env_list: List[Env] = []
26
27
  env_map = dotenv_values(self.env_file)
27
28
  for key, value in env_map.items():
29
+ if key in RESERVED_ENV_NAMES:
30
+ continue
28
31
  os_name: Optional[str] = None
29
32
  if self.prefix is not None and self.prefix != '':
30
33
  os_name = f'{self.prefix}_{key}'
@@ -1 +1 @@
1
- RESERVED_INPUT_NAMES = ('_task', '_args')
1
+ RESERVED_INPUT_NAMES = ('_task', '_args', '_execution_id')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 0.0.108
3
+ Version: 0.0.110
4
4
  Summary: Super framework for your super app
5
5
  Author-email: Go Frendi Gunawan <gofrendiasgard@gmail.com>
6
6
  Requires-Python: >=3.10.0
@@ -30,9 +30,9 @@ Provides-Extra: test
30
30
 
31
31
  # 🤖 Zrb (Read: Zaruba) : A Super Framework for Your Super App
32
32
 
33
- ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/images/zrb/android-chrome-192x192.png)
33
+ ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/zrb/android-chrome-192x192.png)
34
34
 
35
- [📖 Documentation](https://github.com/state-alchemists/zrb/blob/main/docs/README.md) | [🏁 Getting Started](https://github.com/state-alchemists/zrb/blob/main/docs/getting-started.md) | [💃 Oops, I did it Again](https://github.com/state-alchemists/zrb/blob/main/docs/oops-i-did-it-again/README.md)| [❓ FAQ](https://github.com/state-alchemists/zrb/blob/main/docs/faq/README.md)
35
+ [📖 Documentation](https://github.com/state-alchemists/zrb/blob/main/docs/README.md) | [🏁 Getting Started](https://github.com/state-alchemists/zrb/blob/main/docs/getting-started.md) | [💃 Oops, I did it Again](https://github.com/state-alchemists/zrb/blob/main/docs/oops-i-did-it-again/README.md) | [❓ FAQ](https://github.com/state-alchemists/zrb/blob/main/docs/faq/README.md)
36
36
 
37
37
  Zrb is a [CLI-based](https://en.wikipedia.org/wiki/Command-line_interface) automation [tool](https://en.wikipedia.org/wiki/Programming_tool) and [low-code](https://en.wikipedia.org/wiki/Low-code_development_platform) platform. Once installed, Zrb will help you automate day-to-day tasks, generate projects and applications, and even deploy your applications to Kubernetes with a few commands.
38
38
 
@@ -40,7 +40,7 @@ To use Zrb, you need to be familiar with CLI.
40
40
 
41
41
  Zrb task definitions are written in [Python](https://www.python.org/), and we have a [very good reason](https://github.com/state-alchemists/zrb/blob/main/docs/faq/why-python.md) behind the decision.
42
42
 
43
- ## Zrb as a low-code framework
43
+ ## Zrb is A Low-Code Framework
44
44
 
45
45
  Let's see how you can build and run a [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) application.
46
46
 
@@ -69,9 +69,9 @@ zrb project start-fastapp --fastapp-run-mode "monolith"
69
69
 
70
70
  You will be able to access the application by pointing your browser to [http://localhost:3000](http://localhost:3000)
71
71
 
72
- ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/images/fastapp.png)
72
+ ![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/fastapp.png)
73
73
 
74
- Furthermore, you can also run the same application as `microservices`, run the application as `docker containers`, and even doing some deployments into your `kubernetes cluster`.
74
+ Furthermore, you can run the same application as `microservices`, run the application as `docker containers`, and even do some deployments into your `kubernetes cluster`.
75
75
 
76
76
 
77
77
  ```bash
@@ -90,9 +90,9 @@ zrb project deploy-fastapp --fastapp-deploy-mode "microservices"
90
90
  You can visit [our tutorials](https://github.com/state-alchemists/zrb/blob/main/docs/tutorials/README.md) to see more cool tricks.
91
91
 
92
92
 
93
- ## Zrb as a task-automation framework
93
+ ## Zrb is A Task-Automation Tool
94
94
 
95
- Aside from the builtin capabilities, Zrb also allows you to define your automation commands in Python. To do so, you need to create/modify a file named `zrb_init.py`.
95
+ Aside from the built-in capabilities, Zrb also allows you to define your automation commands in Python. To do so, you must create/modify a file named `zrb_init.py`.
96
96
 
97
97
  ```python
98
98
  # filename: zrb_init.py
@@ -133,7 +133,7 @@ To learn more about this, you can visit [our getting started guide](https://gith
133
133
 
134
134
  # 🫰 Installation
135
135
 
136
- ## ⚙️ In local machine
136
+ ## ⚙️ In Local Machine
137
137
 
138
138
  Installing Zrb in your system is as easy as typing the following command in your terminal:
139
139
 
@@ -141,11 +141,11 @@ Installing Zrb in your system is as easy as typing the following command in your
141
141
  pip install zrb
142
142
  ```
143
143
 
144
- Just like any other Python package, you can also install Zrb in your [virtual environment](https://docs.python.org/3/library/venv.html). This will allow you to have many versions of Zrb on the same computer.
144
+ Like any other Python package, you can install Zrb in your [virtual environment](https://docs.python.org/3/library/venv.html). This will allow you to have many versions of Zrb on the same computer.
145
145
 
146
146
  > ⚠️ If the command doesn't work, you probably don't have Pip/Python on your computer. See `Main prerequisites` subsection to install them.
147
147
 
148
- ## 🐋 With docker
148
+ ## 🐋 With Docker
149
149
 
150
150
  If you prefer to work with Docker, you can create a file named `docker-compose.yml`
151
151
 
@@ -184,7 +184,7 @@ You will be able to access Zrb by using docker exec:
184
184
  docker exec -it zrb zsh
185
185
  ```
186
186
 
187
- # ✅ Main prerequisites
187
+ # ✅ Main Prerequisites
188
188
 
189
189
  Since Zrb is written in Python, you need to install a few things before installing Zrb:
190
190
 
@@ -209,11 +209,11 @@ ln -s venv/bin/python3 /usr/local/bin/python
209
209
 
210
210
  If you prefer Python distribution like [conda](https://docs.conda.io/en/latest/), that might work as well.
211
211
 
212
- # ✔️ Other prerequisites
212
+ # ✔️ Other Prerequisites
213
213
 
214
214
  If you want to generate applications using Zrb and run them on your computer, you will also need:
215
215
 
216
- - 🐸 `Node.Js` and `Npm`.
216
+ - 🐸 `Node.Js` and `Npm`.
217
217
  - You need Node.Js to modify/transpile frontend code into static files.
218
218
  - You can visit the [Node.Js website](https://nodejs.org/en) for installation instructions.
219
219
  - 🐋 `Docker` and `Docker-compose` plugin.
@@ -228,9 +228,9 @@ If you want to generate applications using Zrb and run them on your computer, yo
228
228
  - 🦆 `Pulumi`
229
229
  - You need Pulumi to deploy your applications
230
230
 
231
- # 🏁 Getting started
231
+ # 🏁 Getting Started
232
232
 
233
- We have a nice [getting started guide](https://github.com/state-alchemists/zrb/blob/main/docs/getting-started.md) to help you cover the basics. Make sure to check it out😉.
233
+ We have an excellent [getting started guide](https://github.com/state-alchemists/zrb/blob/main/docs/getting-started.md) to help you cover the basics. Make sure to check it out😉.
234
234
 
235
235
  # 📖 Documentation
236
236
 
@@ -240,11 +240,11 @@ You can visit [Zrb documentation](https://github.com/state-alchemists/zrb/blob/m
240
240
 
241
241
  Help Red Skull to click the donation button:
242
242
 
243
- [![](https://raw.githubusercontent.com/state-alchemists/zrb/main/images/donator.png)](https://stalchmst.com/donation)
243
+ [![](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/donator.png)](https://stalchmst.com/donation)
244
244
 
245
- # 🎉 Fun fact
245
+ # 🎉 Fun Fact
246
246
 
247
247
  > Madou Ring Zaruba (魔導輪ザルバ, Madōrin Zaruba) is a Madougu which supports bearers of the Garo Armor. [(Garo Wiki | Fandom)](https://garo.fandom.com/wiki/Zaruba)
248
248
 
249
- ![Madou Ring Zaruba on Kouga's Hand](https://raw.githubusercontent.com/state-alchemists/zrb/main/images/madou-ring-zaruba.jpg)
249
+ ![Madou Ring Zaruba on Kouga's Hand](https://raw.githubusercontent.com/state-alchemists/zrb/main/_images/madou-ring-zaruba.jpg)
250
250
 
@@ -3,13 +3,13 @@ zrb/__main__.py,sha256=CdfuYSxqlJhnsJPOBOL2_uzEaTZHC3MtpyTuz8QUfUI,314
3
3
  zrb/advertisement.py,sha256=e-1tFPlmEuz8IqaIJ_9-2p9x5cuGsxssJGu5F0wHthI,505
4
4
  zrb/runner.py,sha256=MPCNPMCyiYNZeubSxd1eM2Zr6PCIKF-9pkG385AXElw,118
5
5
  zrb/action/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- zrb/action/runner.py,sha256=-bDR_dmsgKO_enhJ7xPE83MhU7-xpakxUtfNgNXdw-U,4474
6
+ zrb/action/runner.py,sha256=JPWERMBRS7FW9Z1m1P2pI0farvdmA6fwX9NyZGJr6Dg,4476
7
7
  zrb/builtin/__init__.py,sha256=VpmpsDJmL2Qe1MDiVTaGgGgW9MxRz8sR21Blclg6MDE,580
8
8
  zrb/builtin/base64.py,sha256=_Ff8eQi0pMa4qqDUqZqA-u-2LpSA5s3AN0Nj2M7ibnQ,1318
9
9
  zrb/builtin/env.py,sha256=hEjQios0i-3DantczxQnZnolzwZAGIQOE0Ygka330EU,1146
10
10
  zrb/builtin/eval.py,sha256=FjAeWuD6KdvSKwsoUo8qbSO9qgc0rQVmRNib-XKOfVc,745
11
11
  zrb/builtin/explain.py,sha256=ee2UjBS-J5-Hf9JDEQR_vs056NiMZCCHtLvE_rrW4AU,5377
12
- zrb/builtin/git.py,sha256=Lz9AFQBr3YEyIO-nuCdeYmNjxq8LVpB-PHE9Kj1lBjg,2679
12
+ zrb/builtin/git.py,sha256=VIhXv8LV12vysSJYitW26mU5p6x9bmTCYknAM4Qu1h0,2616
13
13
  zrb/builtin/group.py,sha256=ncUsZcGRUF9QKNMt-_1zDNDi4Z2KA5jpjwEuYIzhKmo,1077
14
14
  zrb/builtin/md5.py,sha256=IWN0uroXRiuyF0wD05BzGqAkMA3p6q9EbtWmJOPdOc0,1376
15
15
  zrb/builtin/process.py,sha256=h7uMfEIqbOaNe8aTINZYYbHWw7mmUF8DkeiylPeWzSE,1281
@@ -1102,7 +1102,7 @@ zrb/helper/util.py,sha256=82LBcWV8vAnMnYYIfnKq27EfaY_ATxLNJpcePe7VwQI,3469
1102
1102
  zrb/helper/accessories/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1103
1103
  zrb/helper/accessories/color.py,sha256=2G_Ys_iFF7hnnbeLqO3YRBGiYdxnduk0CMcyz4Y8dgg,709
1104
1104
  zrb/helper/accessories/icon.py,sha256=hCL8tSvxxRy7PTKQXsvS4oTPyrP87Zn5RoBgsCq-NLs,347
1105
- zrb/helper/accessories/name.py,sha256=QnZ8dzIaYfxWnS_iOXUlSt497ly4Tw5MsAevYerEerg,1271
1105
+ zrb/helper/accessories/name.py,sha256=DoSTf6SjWOP0GEzWvIalcJeuft8C19v09vfJ1MotTuE,2043
1106
1106
  zrb/helper/codemod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1107
1107
  zrb/helper/codemod/add_argument_to_function.py,sha256=5tuDqcMMozANchrHuUspH5MlRdQj5nw_mwhCzEXb0qY,1265
1108
1108
  zrb/helper/codemod/add_argument_to_function_call.py,sha256=aqQxpmbFbtejJPdjG7v1QIwsaBUT0v_M9ZGhTSxIFKU,1041
@@ -1117,7 +1117,7 @@ zrb/helper/codemod/format_code.py,sha256=ymxZtZm-5RuaddLUu8LJgguX0n8CJSbHb-McQDU
1117
1117
  zrb/helper/docker_compose/fetch_external_env.py,sha256=pIXkMCXtDXVWdNPlpMolqdj4Q0Y73dhCeqwByCB6mWg,2042
1118
1118
  zrb/helper/docker_compose/file.py,sha256=aBtQMV57TWtMO9AwsZ2OyBn16MhElZaDlq54zBqYBnA,685
1119
1119
  zrb/helper/env_map/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1120
- zrb/helper/env_map/fetch.py,sha256=7Oh1BUn71D5QS1BKftqIwopCowjffe2vrDmH3w3oZkg,2160
1120
+ zrb/helper/env_map/fetch.py,sha256=Nl243-dcIEpbImAveZAXo2l0IGNXxD5RN_CyxGYTZeo,2173
1121
1121
  zrb/helper/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1122
1122
  zrb/helper/file/copy_tree.py,sha256=4kVqupzLYyF_neiRsedAFbxkeiK0VYsW45Z2zCkWLXA,1804
1123
1123
  zrb/helper/file/text.py,sha256=_S1uYToMw3lcI95-op_kM_SWNJUyvYNc32XHIcokDWU,807
@@ -1137,18 +1137,18 @@ zrb/shell-scripts/_common-util.sh,sha256=6WPPOp_NBUgIMd1K2qlVZ6iYzZ-Xl3lUTzi4Rqr
1137
1137
  zrb/shell-scripts/ensure-docker-is-installed.sh,sha256=CpfGjfoXFXoTWXsONIGZCMnmtFrk6ZamvO_8Fqp_WJ0,2893
1138
1138
  zrb/shell-scripts/ensure-rsync-is-installed.sh,sha256=vHJWNEidemIrM2tQ17r5qwSUjz9hVKcjYbUEMo3pPJI,987
1139
1139
  zrb/shell-scripts/ensure-ssh-is-installed.sh,sha256=TM0PLXT2vNfgv2ffcW_l-IqtMqHI1Z9nGdJvlToUlyM,2092
1140
- zrb/shell-scripts/rsync-util.sh,sha256=E4Of-AugCFKabQvk-mC0RrQ4JexS7JLT5p6Ux92ETcY,326
1140
+ zrb/shell-scripts/rsync-util.sh,sha256=QzdhSBvUNMxB4U2B4m0Dxg9czGckRjB7Vk4A1ObG0-k,353
1141
1141
  zrb/shell-scripts/ssh-util.sh,sha256=9lXDzw6oO8HuA4vdbfps_uQMMwKyNYX9fZkZgpK52g8,401
1142
1142
  zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1143
- zrb/task/any_task.py,sha256=bG4nVYmEqsf3Kbl78RjIAEv1YHUArpWGMXKiOzIkXa4,5646
1143
+ zrb/task/any_task.py,sha256=fViLjv35ZAuQxBjArv52E6LJgv9vvgihCGEPuiEgv_4,6238
1144
1144
  zrb/task/any_task_event_handler.py,sha256=vpO9t0fck8-oFu1KYptfcg2hXKRsk6V5je4Dg-Bl21o,359
1145
- zrb/task/base_remote_cmd_task.py,sha256=mRizRbkvZwWeBU_MO7qiF89co5Y30ICpxD7cwbpRN3Y,8033
1146
- zrb/task/base_task.py,sha256=yuKfsPzIDHNFd5AUjr9rCStUryHYA_N-R-abJ5c8S38,20878
1147
- zrb/task/base_task_composite.py,sha256=lpckzR6CWmjrGheSBnR1127DTYxk_iqGOPS72bz5F0U,16099
1148
- zrb/task/cmd_task.py,sha256=zpoLkwt77PPeVc1YZCzjq1Kg4jUZ84OR6TpVPK166CM,12804
1145
+ zrb/task/base_remote_cmd_task.py,sha256=M0915WLSjokKrfdLdPhEtXRPKTG1BfWMRTQLEzj6hL4,8930
1146
+ zrb/task/base_task.py,sha256=HxMUb1Iqe4lLOkoSwxCNmRCk2ZE5FoYXmtW8IDy9DkY,21068
1147
+ zrb/task/base_task_composite.py,sha256=mkw6lSYnDqtO5a9y6HGfCWwBcQOfCx-gX8l0-DurC4Q,18756
1148
+ zrb/task/cmd_task.py,sha256=g96LPvWNoYoqGuo4V7ZrzGk8_witf4J0eIKMInwIjvM,12758
1149
1149
  zrb/task/decorator.py,sha256=6mJuDhmyHnVwx8SzBf8el5KwYJdA06VTeb1BABgLmTc,2399
1150
- zrb/task/docker_compose_task.py,sha256=crvPId3c5hJ0kub1pg1DuQigzvMehYva3APRA_jg3HE,13785
1151
- zrb/task/flow_task.py,sha256=w5MaHCQ5KGyongjLo6YjLy9GYM0rTF6yEBGQ4GqbRp0,3933
1150
+ zrb/task/docker_compose_task.py,sha256=aytg_c2_gDyhCIRxGmJONicz_Gw8QPVKWdFCV8rOngI,13152
1151
+ zrb/task/flow_task.py,sha256=lagR8S2tM3y7k5GFoo6qhr9rLJHGCjvVciwMMRxhpzg,3929
1152
1152
  zrb/task/http_checker.py,sha256=ajsBDRgpp2OMeLlgyCL15UECYr2SoKAWfU3PGOJY24Q,5519
1153
1153
  zrb/task/path_checker.py,sha256=EhPJHDPYGAJG3XPUaiN_iN9Y-vtf96gEsWwqagy4IIM,3879
1154
1154
  zrb/task/port_checker.py,sha256=bZ7JPSNoZ1zyZsS05tL4y7soKRbjyU0u3LXsPOE5w7I,4398
@@ -1156,8 +1156,9 @@ zrb/task/remote_cmd_task.py,sha256=LYWmnZ4mPsU-RIdAUyhui7mlIunx4vY8upwjhctnkEs,3
1156
1156
  zrb/task/resource_maker.py,sha256=QwB4S7aFjxX52onCqIu5OOMnm5omoZBl6OqZz9TqSOs,6379
1157
1157
  zrb/task/rsync_task.py,sha256=3vxENH-unD1VLgZI09Ts_lAQRMi2i1UiJpsaI1dltzI,3966
1158
1158
  zrb/task/task.py,sha256=aOwBVPl3U_dXCMcnrtcN0wPR8cDKbYkzgwa86BZMqBE,236
1159
- zrb/task_env/env.py,sha256=1m1GUK4RhdCRfKtEfuCazppQceVAIO05UhJ5Xp-0FOk,1800
1160
- zrb/task_env/env_file.py,sha256=Ku7WnPu-IkWi0fdjjXjTEvleYHfXO5v-g-0RUO2vVk4,1359
1159
+ zrb/task_env/constant.py,sha256=JdPhtZCTZlbW-jzG5vyFtpEXgTJ-Yx863LcrX_LiURY,44
1160
+ zrb/task_env/env.py,sha256=4oS4Y6_qwbzFqO08fzLb0TcSDAe9kxCW5cmbOFO1PSs,1954
1161
+ zrb/task_env/env_file.py,sha256=wQQmIQeK8-bRyl3KYe1xSFfebgHVFjXoXm8mQoVk7dY,1479
1161
1162
  zrb/task_group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1162
1163
  zrb/task_group/group.py,sha256=qPZ79UZzLh5UPqsu2NflE6QAQzV568OPtXHcWloZX1o,1420
1163
1164
  zrb/task_input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1165,14 +1166,14 @@ zrb/task_input/any_input.py,sha256=pLHMnH8VteIXS1ZBuBAVz8_WlAcikTUk1WyU_EEXxX4,5
1165
1166
  zrb/task_input/base_input.py,sha256=98z4bQDvgKlhf8CFMtakbWjvFwn_Y4XKb4YdSB-OTwE,3144
1166
1167
  zrb/task_input/bool_input.py,sha256=8-Kc4Rdc1dykMHxGUuAnRdnRcyEYfVM8ECtZqT071Fc,1594
1167
1168
  zrb/task_input/choice_input.py,sha256=V7BvsI-cI1CCX1DrQQY9bvwEHBYfNvS1WW5QWreMF_0,1672
1168
- zrb/task_input/constant.py,sha256=yvYY-Cc5eRAUQbe5aJcVVy846yyQYYg7v1hgiFyNikg,42
1169
+ zrb/task_input/constant.py,sha256=QgG5alvaRVQYQ5yFNEwj_1DvSmebW0xEDU1JXktSlO4,59
1169
1170
  zrb/task_input/float_input.py,sha256=flaZvFY55us8vtYuBxuAvvr4mTTAVZ-S8uTUt9AGfNg,1595
1170
1171
  zrb/task_input/int_input.py,sha256=mtNrgs65SL0GCzW5qr6c869UjiJ__zJaiXmJZCaqIJs,1592
1171
1172
  zrb/task_input/password_input.py,sha256=5bkUWyPumryigIy-S_z3nBDCo8sQB8_5PeS_gvPI0HQ,1611
1172
1173
  zrb/task_input/str_input.py,sha256=7cubJJKLCWRoGIRIM0_0WUhya18wvURoytrhVZA4xYI,1592
1173
1174
  zrb/task_input/task_input.py,sha256=HotqM1iYSzwE4PIj8grnEUsvJqjx1dS6Ek7i6ZJLq2Y,83
1174
- zrb-0.0.108.dist-info/entry_points.txt,sha256=xTgXc1kBKYhJHEujdaSPHUcJT3-hbyP1mLgwkv-5sSk,40
1175
- zrb-0.0.108.dist-info/LICENSE,sha256=WfnGCl8G60EYOPAEkuc8C9m9pdXWDe08NsKj3TBbxsM,728
1176
- zrb-0.0.108.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
1177
- zrb-0.0.108.dist-info/METADATA,sha256=6y-CGLAw5EcdsecZEIUXHvoAAKMOkedsEg1rNZaEjCk,9755
1178
- zrb-0.0.108.dist-info/RECORD,,
1175
+ zrb-0.0.110.dist-info/entry_points.txt,sha256=xTgXc1kBKYhJHEujdaSPHUcJT3-hbyP1mLgwkv-5sSk,40
1176
+ zrb-0.0.110.dist-info/LICENSE,sha256=WfnGCl8G60EYOPAEkuc8C9m9pdXWDe08NsKj3TBbxsM,728
1177
+ zrb-0.0.110.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
1178
+ zrb-0.0.110.dist-info/METADATA,sha256=YIIg3sAs_V0P_U2aFDdowyznVCsNB1CAdt04_erUeBA,9740
1179
+ zrb-0.0.110.dist-info/RECORD,,
File without changes
File without changes