zrb 0.0.86__py3-none-any.whl → 0.0.87__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. zrb/__init__.py +3 -1
  2. zrb/action/runner.py +7 -3
  3. zrb/builtin/__init__.py +1 -1
  4. zrb/builtin/base64.py +1 -1
  5. zrb/builtin/devtool/devtool_install.py +3 -1
  6. zrb/builtin/env.py +6 -3
  7. zrb/builtin/explain.py +3 -1
  8. zrb/builtin/generator/__init__.py +4 -8
  9. zrb/builtin/generator/app_generator/add.py +6 -6
  10. zrb/builtin/generator/app_generator/template/_automate/snake_zrb_meta_template_name/add.py +60 -50
  11. zrb/builtin/generator/app_generator/template/_automate/snake_zrb_meta_template_name/template/_automate/snake_zrb_app_name/container.py +1 -1
  12. zrb/builtin/generator/app_generator/template/_automate/snake_zrb_meta_template_name/template/_automate/snake_zrb_app_name/deployment.py +2 -2
  13. zrb/builtin/generator/app_generator/template/_automate/snake_zrb_meta_template_name/template/_automate/snake_zrb_app_name/image.py +1 -1
  14. zrb/builtin/generator/app_generator/template/_automate/snake_zrb_meta_template_name/template/_automate/snake_zrb_app_name/local.py +4 -2
  15. zrb/builtin/generator/cmd_task/add.py +6 -6
  16. zrb/builtin/generator/cmd_task/template/_automate/snake_zrb_task_name.py +1 -1
  17. zrb/builtin/generator/{_common → common}/helper.py +6 -0
  18. zrb/builtin/generator/{_common → common}/task_factory.py +9 -5
  19. zrb/builtin/generator/docker_compose_task/add.py +8 -8
  20. zrb/builtin/generator/docker_compose_task/template/_automate/snake_zrb_task_name.py +1 -1
  21. zrb/builtin/generator/fastapp/add.py +24 -24
  22. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/_common.py +2 -1
  23. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/container.py +1 -1
  24. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/deployment.py +1 -1
  25. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/frontend.py +1 -1
  26. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/image.py +1 -1
  27. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/load_test.py +3 -2
  28. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/local.py +1 -1
  29. zrb/builtin/generator/fastapp/template/_automate/snake_zrb_app_name/test.py +2 -4
  30. zrb/builtin/generator/fastapp_crud/add.py +8 -121
  31. zrb/builtin/generator/fastapp_crud/helper.py +115 -0
  32. zrb/builtin/generator/fastapp_crud/{add_navigation.py → task_factory.py} +5 -3
  33. zrb/builtin/generator/fastapp_field/add.py +8 -280
  34. zrb/builtin/generator/fastapp_field/helper.py +283 -0
  35. zrb/builtin/generator/fastapp_module/add.py +19 -328
  36. zrb/builtin/generator/fastapp_module/helper.py +326 -0
  37. zrb/builtin/generator/pip_package/add.py +8 -8
  38. zrb/builtin/generator/pip_package/template/_automate/snake_zrb_package_name/local.py +1 -1
  39. zrb/builtin/generator/project/create.py +5 -3
  40. zrb/builtin/generator/project_task/task_factory.py +33 -21
  41. zrb/builtin/generator/project_task/template/_automate/_project/build_project_images.py +1 -1
  42. zrb/builtin/generator/project_task/template/_automate/_project/deploy_project.py +1 -1
  43. zrb/builtin/generator/project_task/template/_automate/_project/destroy_project.py +1 -1
  44. zrb/builtin/generator/project_task/template/_automate/_project/push_project_images.py +1 -1
  45. zrb/builtin/generator/project_task/template/_automate/_project/remove_project_containers.py +1 -1
  46. zrb/builtin/generator/project_task/template/_automate/_project/start_project.py +1 -1
  47. zrb/builtin/generator/project_task/template/_automate/_project/start_project_containers.py +1 -1
  48. zrb/builtin/generator/project_task/template/_automate/_project/stop_project_containers.py +1 -1
  49. zrb/builtin/generator/python_task/add.py +6 -6
  50. zrb/builtin/generator/python_task/template/_automate/snake_zrb_task_name.py +1 -1
  51. zrb/builtin/generator/simple_python_app/add.py +28 -30
  52. zrb/builtin/generator/simple_python_app/template/_automate/snake_zrb_app_name/container.py +1 -1
  53. zrb/builtin/generator/simple_python_app/template/_automate/snake_zrb_app_name/deployment.py +1 -1
  54. zrb/builtin/generator/simple_python_app/template/_automate/snake_zrb_app_name/image.py +1 -1
  55. zrb/builtin/generator/simple_python_app/template/_automate/snake_zrb_app_name/local.py +1 -1
  56. zrb/builtin/git.py +1 -1
  57. zrb/builtin/md5.py +1 -1
  58. zrb/builtin/project.py +1 -1
  59. zrb/builtin/ubuntu.py +1 -1
  60. zrb/config/config.py +8 -6
  61. zrb/helper/accessories/color.py +5 -1
  62. zrb/helper/accessories/icon.py +2 -0
  63. zrb/helper/accessories/name.py +2 -0
  64. zrb/helper/advertisement.py +1 -0
  65. zrb/helper/cli.py +5 -1
  66. zrb/helper/codemod/add_argument_to_function.py +3 -0
  67. zrb/helper/codemod/add_argument_to_function_call.py +3 -0
  68. zrb/helper/codemod/add_assert_resource.py +2 -0
  69. zrb/helper/codemod/add_function_call.py +2 -0
  70. zrb/helper/codemod/add_import_module.py +3 -0
  71. zrb/helper/codemod/add_key_value_to_dict.py +2 -0
  72. zrb/helper/codemod/add_property_to_class.py +3 -0
  73. zrb/helper/codemod/add_upstream_to_task.py +3 -0
  74. zrb/helper/codemod/append_code_to_function.py +3 -0
  75. zrb/helper/codemod/format_code.py +2 -0
  76. zrb/helper/default_env.py +2 -0
  77. zrb/helper/docker_compose/fetch_external_env.py +3 -0
  78. zrb/helper/docker_compose/file.py +4 -0
  79. zrb/helper/env_map/fetch.py +7 -0
  80. zrb/helper/file/text.py +4 -0
  81. zrb/helper/git/detect_changes.py +4 -0
  82. zrb/helper/loader/load_module.py +2 -0
  83. zrb/helper/map/conversion.py +2 -0
  84. zrb/helper/string/conversion.py +5 -0
  85. zrb/helper/string/double_quote.py +6 -2
  86. zrb/helper/string/jinja.py +2 -0
  87. zrb/helper/string/parse_replacement.py +2 -0
  88. zrb/helper/util.py +13 -0
  89. zrb/task/any_task.py +2 -2
  90. zrb/task/base_task.py +15 -15
  91. zrb/task/base_task_composite.py +3 -3
  92. zrb/task/cmd_task.py +2 -2
  93. zrb/task/decorator.py +2 -2
  94. zrb/task/docker_compose_task.py +2 -2
  95. zrb/task/flow_task.py +5 -5
  96. zrb/task/http_checker.py +2 -2
  97. zrb/task/path_checker.py +2 -2
  98. zrb/task/port_checker.py +2 -2
  99. zrb/task/resource_maker.py +3 -5
  100. zrb/task_input/any_input.py +23 -0
  101. zrb/task_input/base_input.py +46 -43
  102. zrb/task_input/float_input.py +0 -1
  103. {zrb-0.0.86.dist-info → zrb-0.0.87.dist-info}/METADATA +1 -1
  104. {zrb-0.0.86.dist-info → zrb-0.0.87.dist-info}/RECORD +111 -108
  105. zrb/config.toml +0 -0
  106. /zrb/builtin/generator/{_common → common}/__init__.py +0 -0
  107. /zrb/builtin/generator/{_common → common}/task_input.py +0 -0
  108. /zrb/builtin/{_group.py → group.py} +0 -0
  109. /zrb/task_input/{_constant.py → constant.py} +0 -0
  110. {zrb-0.0.86.dist-info → zrb-0.0.87.dist-info}/LICENSE +0 -0
  111. {zrb-0.0.86.dist-info → zrb-0.0.87.dist-info}/WHEEL +0 -0
  112. {zrb-0.0.86.dist-info → zrb-0.0.87.dist-info}/entry_points.txt +0 -0
@@ -1,27 +1,22 @@
1
- from typing import Any, List, Mapping
2
- from dotenv import dotenv_values
3
- from ..._group import project_add_group
1
+ from typing import Any
2
+ from .helper import (
3
+ create_microservice_config, register_module, register_migration,
4
+ create_app_config, append_all_enabled_env, append_all_disabled_env,
5
+ append_src_template_env, append_deployment_template_env
6
+ )
7
+ from ..common.helper import validate_existing_project_dir
8
+ from ..common.task_input import (
9
+ project_dir_input, app_name_input, module_name_input
10
+ )
11
+ from ...group import project_add_group
4
12
  from ....task.task import Task
5
13
  from ....task.decorator import python_task
6
14
  from ....task.resource_maker import ResourceMaker
7
15
  from ....runner import runner
8
- from .._common.task_input import (
9
- project_dir_input, app_name_input,
10
- module_name_input
11
- )
12
- from .._common.helper import validate_existing_project_dir
13
16
  from ....helper import util
14
- from ....helper.codemod.add_import_module import add_import_module
15
- from ....helper.codemod.add_function_call import add_function_call
16
- from ....helper.codemod.append_code_to_function import append_code_to_function
17
- from ....helper.docker_compose.file import add_services
18
- from ....helper.file.text import (
19
- read_text_file_async, write_text_file_async, append_text_file_async
20
- )
21
17
 
22
18
  import asyncio
23
19
  import os
24
- import jsons
25
20
 
26
21
  CURRENT_DIR = os.path.dirname(__file__)
27
22
 
@@ -95,335 +90,31 @@ async def add_fastapp_module(*args: Any, **kwargs: Any):
95
90
  snake_module_name = util.to_snake_case(module_name)
96
91
  upper_snake_module_name = snake_module_name.upper()
97
92
  await asyncio.gather(
98
- asyncio.create_task(_create_microservice_config(
93
+ asyncio.create_task(create_microservice_config(
99
94
  task, project_dir, kebab_app_name, snake_app_name,
100
95
  kebab_module_name, snake_module_name, upper_snake_module_name
101
96
  )),
102
- asyncio.create_task(_register_module(
97
+ asyncio.create_task(register_module(
103
98
  task, project_dir, kebab_app_name, snake_module_name
104
99
  )),
105
- asyncio.create_task(_register_migration(
100
+ asyncio.create_task(register_migration(
106
101
  task, project_dir, kebab_app_name, snake_module_name
107
102
  )),
108
- asyncio.create_task(_create_app_config(
103
+ asyncio.create_task(create_app_config(
109
104
  task, project_dir, kebab_app_name, snake_module_name,
110
105
  upper_snake_module_name
111
106
  )),
112
- asyncio.create_task(_append_all_enabled_env(
107
+ asyncio.create_task(append_all_enabled_env(
113
108
  task, project_dir, kebab_app_name, upper_snake_module_name
114
109
  )),
115
- asyncio.create_task(_append_all_disabled_env(
110
+ asyncio.create_task(append_all_disabled_env(
116
111
  task, project_dir, kebab_app_name, upper_snake_module_name
117
112
  )),
118
- asyncio.create_task(_append_src_template_env(
113
+ asyncio.create_task(append_src_template_env(
119
114
  task, project_dir, kebab_app_name, upper_snake_module_name
120
115
  )),
121
- asyncio.create_task(_append_deployment_template_env(
116
+ asyncio.create_task(append_deployment_template_env(
122
117
  task, project_dir, kebab_app_name, upper_snake_module_name
123
118
  )),
124
119
  )
125
120
  task.print_out('Success')
126
-
127
-
128
- ###############################################################################
129
- # Helper Definitions
130
- ###############################################################################
131
-
132
-
133
- async def _create_microservice_config(
134
- task: Task,
135
- project_dir: str,
136
- kebab_app_name: str,
137
- snake_app_name: str,
138
- kebab_module_name: str,
139
- snake_module_name: str,
140
- upper_snake_module_name: str
141
- ):
142
- modules = await _create_automation_json_config(
143
- task, project_dir, snake_app_name, snake_module_name
144
- )
145
- return await asyncio.gather(
146
- asyncio.create_task(_add_docker_compose_service(
147
- task, modules, project_dir, kebab_app_name,
148
- snake_app_name, kebab_module_name, snake_module_name,
149
- upper_snake_module_name
150
- )),
151
- asyncio.create_task(_append_compose_env(
152
- task, modules, project_dir, kebab_app_name, upper_snake_module_name
153
- ))
154
- )
155
-
156
-
157
- async def _add_docker_compose_service(
158
- task: Task,
159
- modules: List[str],
160
- project_dir: str,
161
- kebab_app_name: str,
162
- snake_app_name: str,
163
- kebab_module_name: str,
164
- snake_module_name: str,
165
- upper_snake_module_name: str
166
- ):
167
- module_app_port = 8080 + len(modules)
168
- module_app_port_str = str(module_app_port)
169
- docker_compose_file_path = os.path.join(
170
- project_dir, 'src', kebab_app_name, 'docker-compose.yml'
171
- )
172
- app_container_port_env_name = f'APP_{upper_snake_module_name}_MODULE_PORT'
173
- app_container_port_env = '${' + app_container_port_env_name + ':-' + module_app_port_str + '}' # noqa
174
- app_host_port_env_name = f'APP_{upper_snake_module_name}_HOST_MODULE_PORT'
175
- app_host_port_env = '${' + app_host_port_env_name + ':-' + module_app_port_str + '}' # noqa
176
- service_definition = _get_new_docker_compose_service_definition(
177
- kebab_app_name, snake_app_name, kebab_module_name, snake_module_name,
178
- upper_snake_module_name, app_host_port_env, app_container_port_env
179
- )
180
- task.print_out(f'Add service at: {docker_compose_file_path}')
181
- add_services(
182
- file_name=docker_compose_file_path,
183
- new_services=service_definition,
184
- )
185
-
186
-
187
- def _get_new_docker_compose_service_definition(
188
- kebab_app_name: str,
189
- snake_app_name: str,
190
- kebab_module_name: str,
191
- snake_module_name: str,
192
- upper_snake_module_name: str,
193
- app_host_port_env: str,
194
- app_container_port_env: str
195
- ) -> Mapping[str, Any]:
196
- return {
197
- f'{kebab_app_name}-{kebab_module_name}-service': {
198
- 'build': {
199
- 'dockerfile': 'Dockerfile',
200
- 'context': './src'
201
- },
202
- 'image': '${IMAGE:-' + kebab_app_name + '}',
203
- 'container_name': '${CONTAINER_PREFIX:-my}-' + f'{kebab_app_name}-{kebab_module_name}-service', # noqa
204
- 'hostname': f'{kebab_app_name}-{kebab_module_name}-service',
205
- 'env_file': [
206
- 'src/template.env',
207
- 'all-module-disabled.env'
208
- ],
209
- 'environment': {
210
- 'APP_NAME': '${APP_NAME:-' + kebab_app_name + '}-' + f'{kebab_module_name}-service', # noqa
211
- 'APP_PORT': app_container_port_env,
212
- 'APP_ENABLE_EVENT_HANDLER': 'true',
213
- 'APP_ENABLE_RPC_SERVER': 'true',
214
- 'APP_ENABLE_API': 'false',
215
- 'APP_ENABLE_FRONTEND': 'false',
216
- f'APP_ENABLE_{upper_snake_module_name}_MODULE': 'true',
217
- },
218
- 'ports': [
219
- f'{app_host_port_env}:{app_container_port_env}'
220
- ],
221
- 'restart': 'unless-stopped',
222
- 'profiles': [
223
- 'microservices'
224
- ],
225
- 'healthcheck': {
226
- 'test': [
227
- 'CMD-SHELL',
228
- 'curl --fail http://localhost:' + app_container_port_env + '/readiness || exit 1' # noqa
229
- ],
230
- 'interval': '5s',
231
- 'timeout': '3s',
232
- 'retries': 10
233
- },
234
- 'networks': ['zrb']
235
- }
236
- }
237
-
238
-
239
- async def _append_compose_env(
240
- task: Task,
241
- modules: List[str],
242
- project_dir: str,
243
- kebab_app_name: str,
244
- upper_snake_module_name: str
245
- ):
246
- compose_template_env_path = os.path.join(
247
- project_dir, 'src', kebab_app_name, 'docker-compose.env'
248
- )
249
- compose_env_map = dotenv_values(compose_template_env_path)
250
- host_port = int(compose_env_map.get('APP_GATEWAY_HOST_PORT', '8080'))
251
- module_app_port = host_port + len(modules)
252
- new_env_str = '\n'.join([
253
- f'APP_{upper_snake_module_name}_HOST_MODULE_PORT={module_app_port}',
254
- f'APP_{upper_snake_module_name}_MODULE_PORT={module_app_port}',
255
- ])
256
- task.print_out(f'Add new environment to: {compose_template_env_path}')
257
- await append_text_file_async(compose_template_env_path, new_env_str)
258
-
259
-
260
- async def _append_src_template_env(
261
- task: Task,
262
- project_dir: str,
263
- kebab_app_name: str,
264
- upper_snake_module_name: str
265
- ):
266
- src_template_env_path = os.path.join(
267
- project_dir, 'src', kebab_app_name, 'src', 'template.env'
268
- )
269
- new_env_str = '\n'.join([
270
- f'APP_ENABLE_{upper_snake_module_name}_MODULE=true',
271
- ])
272
- task.print_out(f'Add new environment to: {src_template_env_path}')
273
- await append_text_file_async(src_template_env_path, new_env_str)
274
-
275
-
276
- async def _append_deployment_template_env(
277
- task: Task,
278
- project_dir: str,
279
- kebab_app_name: str,
280
- upper_snake_module_name: str
281
- ):
282
- deployment_template_env_path = os.path.join(
283
- project_dir, 'src', kebab_app_name, 'deployment', 'template.env'
284
- )
285
- new_env_str = '\n'.join([
286
- f'REPLICA_{upper_snake_module_name}_SERVICE=1',
287
- ])
288
- task.print_out(f'Add new environment to: {deployment_template_env_path}')
289
- await append_text_file_async(deployment_template_env_path, new_env_str)
290
-
291
-
292
- async def _append_all_enabled_env(
293
- task: Task,
294
- project_dir: str,
295
- kebab_app_name: str,
296
- upper_snake_module_name: str
297
- ):
298
- all_enabled_env_path = os.path.join(
299
- project_dir, 'src', kebab_app_name, 'all-module-enabled.env'
300
- )
301
- task.print_out(f'Add new environment to: {all_enabled_env_path}')
302
- await append_text_file_async(
303
- all_enabled_env_path,
304
- f'APP_ENABLE_{upper_snake_module_name}_MODULE=true'
305
- )
306
-
307
-
308
- async def _append_all_disabled_env(
309
- task: Task,
310
- project_dir: str,
311
- kebab_app_name: str,
312
- upper_snake_module_name: str
313
- ):
314
- all_disabled_env_path = os.path.join(
315
- project_dir, 'src', kebab_app_name, 'all-module-disabled.env'
316
- )
317
- task.print_out(f'Add new environment to: {all_disabled_env_path}')
318
- await append_text_file_async(
319
- all_disabled_env_path,
320
- f'APP_ENABLE_{upper_snake_module_name}_MODULE=false'
321
- )
322
-
323
-
324
- async def _create_app_config(
325
- task: Task,
326
- project_dir: str,
327
- kebab_app_name: str,
328
- snake_module_name: str,
329
- upper_snake_module_name: str
330
- ):
331
- config_file_path = os.path.join(
332
- project_dir, 'src', kebab_app_name, 'src', 'config.py'
333
- )
334
- config_code = '\n'.join([
335
- f'app_enable_{snake_module_name}_module = str_to_boolean(os.environ.get(', # noqa
336
- f" 'APP_ENABLE_{upper_snake_module_name}_MODULE', 'true'"
337
- '))'
338
- ])
339
- task.print_out(f'Read config from: {config_file_path}')
340
- code = await read_text_file_async(config_file_path)
341
- code += '\n' + config_code
342
- task.print_out(f'Write config to: {config_file_path}')
343
- await write_text_file_async(config_file_path, code)
344
-
345
-
346
- async def _create_automation_json_config(
347
- task: Task,
348
- project_dir: str,
349
- snake_app_name: str,
350
- snake_module_name: str,
351
- ):
352
- json_modules_file_path = os.path.join(
353
- project_dir, '_automate', snake_app_name, 'config', 'modules.json'
354
- )
355
- task.print_out(f'Read json config from: {json_modules_file_path}')
356
- json_str = await read_text_file_async(json_modules_file_path)
357
- task.print_out(f'Add "{snake_module_name}" to json config')
358
- modules: List[str] = jsons.loads(json_str)
359
- modules.append(snake_module_name)
360
- json_str = jsons.dumps(modules)
361
- task.print_out(f'Write new json config to: {json_modules_file_path}')
362
- await write_text_file_async(json_modules_file_path, json_str)
363
- return modules
364
-
365
-
366
- async def _register_migration(
367
- task: Task,
368
- project_dir: str,
369
- kebab_app_name: str,
370
- snake_module_name: str
371
- ):
372
- app_migration_file_path = os.path.join(
373
- project_dir, 'src', kebab_app_name, 'src', 'migrate.py'
374
- )
375
- import_module_path = '.'.join([
376
- 'module', snake_module_name, 'migrate'
377
- ])
378
- function_name = f'migrate_{snake_module_name}'
379
- task.print_out(f'Read code from: {app_migration_file_path}')
380
- code = await read_text_file_async(app_migration_file_path)
381
- task.print_out(
382
- f'Add import "{function_name}" from "{import_module_path}" to the code'
383
- )
384
- code = add_import_module(
385
- code=code,
386
- module_path=import_module_path,
387
- resource=function_name
388
- )
389
- task.print_out(f'Add "{function_name}" call to the code')
390
- code = append_code_to_function(
391
- code=code,
392
- function_name='migrate',
393
- new_code=f'await {function_name}()'
394
- )
395
- task.print_out(f'Write modified code to: {app_migration_file_path}')
396
- await write_text_file_async(app_migration_file_path, code)
397
-
398
-
399
- async def _register_module(
400
- task: Task,
401
- project_dir: str,
402
- kebab_app_name: str,
403
- snake_module_name: str
404
- ):
405
- app_main_file_path = os.path.join(
406
- project_dir, 'src', kebab_app_name, 'src', 'main.py'
407
- )
408
- import_module_path = '.'.join([
409
- 'module', snake_module_name, 'register_module'
410
- ])
411
- function_name = f'register_{snake_module_name}'
412
- task.print_out(f'Read code from: {app_main_file_path}')
413
- code = await read_text_file_async(app_main_file_path)
414
- task.print_out(
415
- f'Add import "{function_name}" from "{import_module_path}" to the code'
416
- )
417
- code = add_import_module(
418
- code=code,
419
- module_path=import_module_path,
420
- resource=function_name
421
- )
422
- task.print_out(f'Add "{function_name}" call to the code')
423
- code = add_function_call(
424
- code=code,
425
- function_name=function_name,
426
- parameters=[]
427
- )
428
- task.print_out(f'Write modified code to: {app_main_file_path}')
429
- await write_text_file_async(app_main_file_path, code)
@@ -0,0 +1,326 @@
1
+ from typeguard import typechecked
2
+ from typing import Any, List, Mapping
3
+ from dotenv import dotenv_values
4
+ from ....task.task import Task
5
+ from ....helper.codemod.add_import_module import add_import_module
6
+ from ....helper.codemod.add_function_call import add_function_call
7
+ from ....helper.codemod.append_code_to_function import append_code_to_function
8
+ from ....helper.docker_compose.file import add_services
9
+ from ....helper.file.text import (
10
+ read_text_file_async, write_text_file_async, append_text_file_async
11
+ )
12
+
13
+ import asyncio
14
+ import os
15
+ import jsons
16
+
17
+
18
+ @typechecked
19
+ async def create_microservice_config(
20
+ task: Task,
21
+ project_dir: str,
22
+ kebab_app_name: str,
23
+ snake_app_name: str,
24
+ kebab_module_name: str,
25
+ snake_module_name: str,
26
+ upper_snake_module_name: str
27
+ ):
28
+ modules = await _create_automation_json_config(
29
+ task, project_dir, snake_app_name, snake_module_name
30
+ )
31
+ return await asyncio.gather(
32
+ asyncio.create_task(_add_docker_compose_service(
33
+ task, modules, project_dir, kebab_app_name,
34
+ snake_app_name, kebab_module_name, snake_module_name,
35
+ upper_snake_module_name
36
+ )),
37
+ asyncio.create_task(_append_compose_env(
38
+ task, modules, project_dir, kebab_app_name, upper_snake_module_name
39
+ ))
40
+ )
41
+
42
+
43
+ @typechecked
44
+ async def _add_docker_compose_service(
45
+ task: Task,
46
+ modules: List[str],
47
+ project_dir: str,
48
+ kebab_app_name: str,
49
+ snake_app_name: str,
50
+ kebab_module_name: str,
51
+ snake_module_name: str,
52
+ upper_snake_module_name: str
53
+ ):
54
+ module_app_port = 8080 + len(modules)
55
+ module_app_port_str = str(module_app_port)
56
+ docker_compose_file_path = os.path.join(
57
+ project_dir, 'src', kebab_app_name, 'docker-compose.yml'
58
+ )
59
+ app_container_port_env_name = f'APP_{upper_snake_module_name}_MODULE_PORT'
60
+ app_container_port_env = '${' + app_container_port_env_name + ':-' + module_app_port_str + '}' # noqa
61
+ app_host_port_env_name = f'APP_{upper_snake_module_name}_HOST_MODULE_PORT'
62
+ app_host_port_env = '${' + app_host_port_env_name + ':-' + module_app_port_str + '}' # noqa
63
+ service_definition = _get_new_docker_compose_service_definition(
64
+ kebab_app_name, snake_app_name, kebab_module_name, snake_module_name,
65
+ upper_snake_module_name, app_host_port_env, app_container_port_env
66
+ )
67
+ task.print_out(f'Add service at: {docker_compose_file_path}')
68
+ add_services(
69
+ file_name=docker_compose_file_path,
70
+ new_services=service_definition,
71
+ )
72
+
73
+
74
+ @typechecked
75
+ def _get_new_docker_compose_service_definition(
76
+ kebab_app_name: str,
77
+ snake_app_name: str,
78
+ kebab_module_name: str,
79
+ snake_module_name: str,
80
+ upper_snake_module_name: str,
81
+ app_host_port_env: str,
82
+ app_container_port_env: str
83
+ ) -> Mapping[str, Any]:
84
+ return {
85
+ f'{kebab_app_name}-{kebab_module_name}-service': {
86
+ 'build': {
87
+ 'dockerfile': 'Dockerfile',
88
+ 'context': './src'
89
+ },
90
+ 'image': '${IMAGE:-' + kebab_app_name + '}',
91
+ 'container_name': '${CONTAINER_PREFIX:-my}-' + f'{kebab_app_name}-{kebab_module_name}-service', # noqa
92
+ 'hostname': f'{kebab_app_name}-{kebab_module_name}-service',
93
+ 'env_file': [
94
+ 'src/template.env',
95
+ 'all-module-disabled.env'
96
+ ],
97
+ 'environment': {
98
+ 'APP_NAME': '${APP_NAME:-' + kebab_app_name + '}-' + f'{kebab_module_name}-service', # noqa
99
+ 'APP_PORT': app_container_port_env,
100
+ 'APP_ENABLE_EVENT_HANDLER': 'true',
101
+ 'APP_ENABLE_RPC_SERVER': 'true',
102
+ 'APP_ENABLE_API': 'false',
103
+ 'APP_ENABLE_FRONTEND': 'false',
104
+ f'APP_ENABLE_{upper_snake_module_name}_MODULE': 'true',
105
+ },
106
+ 'ports': [
107
+ f'{app_host_port_env}:{app_container_port_env}'
108
+ ],
109
+ 'restart': 'unless-stopped',
110
+ 'profiles': [
111
+ 'microservices'
112
+ ],
113
+ 'healthcheck': {
114
+ 'test': [
115
+ 'CMD-SHELL',
116
+ 'curl --fail http://localhost:' + app_container_port_env + '/readiness || exit 1' # noqa
117
+ ],
118
+ 'interval': '5s',
119
+ 'timeout': '3s',
120
+ 'retries': 10
121
+ },
122
+ 'networks': ['zrb']
123
+ }
124
+ }
125
+
126
+
127
+ @typechecked
128
+ async def _append_compose_env(
129
+ task: Task,
130
+ modules: List[str],
131
+ project_dir: str,
132
+ kebab_app_name: str,
133
+ upper_snake_module_name: str
134
+ ):
135
+ compose_template_env_path = os.path.join(
136
+ project_dir, 'src', kebab_app_name, 'docker-compose.env'
137
+ )
138
+ compose_env_map = dotenv_values(compose_template_env_path)
139
+ host_port = int(compose_env_map.get('APP_GATEWAY_HOST_PORT', '8080'))
140
+ module_app_port = host_port + len(modules)
141
+ new_env_str = '\n'.join([
142
+ f'APP_{upper_snake_module_name}_HOST_MODULE_PORT={module_app_port}',
143
+ f'APP_{upper_snake_module_name}_MODULE_PORT={module_app_port}',
144
+ ])
145
+ task.print_out(f'Add new environment to: {compose_template_env_path}')
146
+ await append_text_file_async(compose_template_env_path, new_env_str)
147
+
148
+
149
+ @typechecked
150
+ async def append_src_template_env(
151
+ task: Task,
152
+ project_dir: str,
153
+ kebab_app_name: str,
154
+ upper_snake_module_name: str
155
+ ):
156
+ src_template_env_path = os.path.join(
157
+ project_dir, 'src', kebab_app_name, 'src', 'template.env'
158
+ )
159
+ new_env_str = '\n'.join([
160
+ f'APP_ENABLE_{upper_snake_module_name}_MODULE=true',
161
+ ])
162
+ task.print_out(f'Add new environment to: {src_template_env_path}')
163
+ await append_text_file_async(src_template_env_path, new_env_str)
164
+
165
+
166
+ @typechecked
167
+ async def append_deployment_template_env(
168
+ task: Task,
169
+ project_dir: str,
170
+ kebab_app_name: str,
171
+ upper_snake_module_name: str
172
+ ):
173
+ deployment_template_env_path = os.path.join(
174
+ project_dir, 'src', kebab_app_name, 'deployment', 'template.env'
175
+ )
176
+ new_env_str = '\n'.join([
177
+ f'REPLICA_{upper_snake_module_name}_SERVICE=1',
178
+ ])
179
+ task.print_out(f'Add new environment to: {deployment_template_env_path}')
180
+ await append_text_file_async(deployment_template_env_path, new_env_str)
181
+
182
+
183
+ @typechecked
184
+ async def append_all_enabled_env(
185
+ task: Task,
186
+ project_dir: str,
187
+ kebab_app_name: str,
188
+ upper_snake_module_name: str
189
+ ):
190
+ all_enabled_env_path = os.path.join(
191
+ project_dir, 'src', kebab_app_name, 'all-module-enabled.env'
192
+ )
193
+ task.print_out(f'Add new environment to: {all_enabled_env_path}')
194
+ await append_text_file_async(
195
+ all_enabled_env_path,
196
+ f'APP_ENABLE_{upper_snake_module_name}_MODULE=true'
197
+ )
198
+
199
+
200
+ @typechecked
201
+ async def append_all_disabled_env(
202
+ task: Task,
203
+ project_dir: str,
204
+ kebab_app_name: str,
205
+ upper_snake_module_name: str
206
+ ):
207
+ all_disabled_env_path = os.path.join(
208
+ project_dir, 'src', kebab_app_name, 'all-module-disabled.env'
209
+ )
210
+ task.print_out(f'Add new environment to: {all_disabled_env_path}')
211
+ await append_text_file_async(
212
+ all_disabled_env_path,
213
+ f'APP_ENABLE_{upper_snake_module_name}_MODULE=false'
214
+ )
215
+
216
+
217
+ @typechecked
218
+ async def create_app_config(
219
+ task: Task,
220
+ project_dir: str,
221
+ kebab_app_name: str,
222
+ snake_module_name: str,
223
+ upper_snake_module_name: str
224
+ ):
225
+ config_file_path = os.path.join(
226
+ project_dir, 'src', kebab_app_name, 'src', 'config.py'
227
+ )
228
+ config_code = '\n'.join([
229
+ f'app_enable_{snake_module_name}_module = str_to_boolean(os.environ.get(', # noqa
230
+ f" 'APP_ENABLE_{upper_snake_module_name}_MODULE', 'true'"
231
+ '))'
232
+ ])
233
+ task.print_out(f'Read config from: {config_file_path}')
234
+ code = await read_text_file_async(config_file_path)
235
+ code += '\n' + config_code
236
+ task.print_out(f'Write config to: {config_file_path}')
237
+ await write_text_file_async(config_file_path, code)
238
+
239
+
240
+ @typechecked
241
+ async def _create_automation_json_config(
242
+ task: Task,
243
+ project_dir: str,
244
+ snake_app_name: str,
245
+ snake_module_name: str,
246
+ ):
247
+ json_modules_file_path = os.path.join(
248
+ project_dir, '_automate', snake_app_name, 'config', 'modules.json'
249
+ )
250
+ task.print_out(f'Read json config from: {json_modules_file_path}')
251
+ json_str = await read_text_file_async(json_modules_file_path)
252
+ task.print_out(f'Add "{snake_module_name}" to json config')
253
+ modules: List[str] = jsons.loads(json_str)
254
+ modules.append(snake_module_name)
255
+ json_str = jsons.dumps(modules)
256
+ task.print_out(f'Write new json config to: {json_modules_file_path}')
257
+ await write_text_file_async(json_modules_file_path, json_str)
258
+ return modules
259
+
260
+
261
+ @typechecked
262
+ async def register_migration(
263
+ task: Task,
264
+ project_dir: str,
265
+ kebab_app_name: str,
266
+ snake_module_name: str
267
+ ):
268
+ app_migration_file_path = os.path.join(
269
+ project_dir, 'src', kebab_app_name, 'src', 'migrate.py'
270
+ )
271
+ import_module_path = '.'.join([
272
+ 'module', snake_module_name, 'migrate'
273
+ ])
274
+ function_name = f'migrate_{snake_module_name}'
275
+ task.print_out(f'Read code from: {app_migration_file_path}')
276
+ code = await read_text_file_async(app_migration_file_path)
277
+ task.print_out(
278
+ f'Add import "{function_name}" from "{import_module_path}" to the code'
279
+ )
280
+ code = add_import_module(
281
+ code=code,
282
+ module_path=import_module_path,
283
+ resource=function_name
284
+ )
285
+ task.print_out(f'Add "{function_name}" call to the code')
286
+ code = append_code_to_function(
287
+ code=code,
288
+ function_name='migrate',
289
+ new_code=f'await {function_name}()'
290
+ )
291
+ task.print_out(f'Write modified code to: {app_migration_file_path}')
292
+ await write_text_file_async(app_migration_file_path, code)
293
+
294
+
295
+ @typechecked
296
+ async def register_module(
297
+ task: Task,
298
+ project_dir: str,
299
+ kebab_app_name: str,
300
+ snake_module_name: str
301
+ ):
302
+ app_main_file_path = os.path.join(
303
+ project_dir, 'src', kebab_app_name, 'src', 'main.py'
304
+ )
305
+ import_module_path = '.'.join([
306
+ 'module', snake_module_name, 'register_module'
307
+ ])
308
+ function_name = f'register_{snake_module_name}'
309
+ task.print_out(f'Read code from: {app_main_file_path}')
310
+ code = await read_text_file_async(app_main_file_path)
311
+ task.print_out(
312
+ f'Add import "{function_name}" from "{import_module_path}" to the code'
313
+ )
314
+ code = add_import_module(
315
+ code=code,
316
+ module_path=import_module_path,
317
+ resource=function_name
318
+ )
319
+ task.print_out(f'Add "{function_name}" call to the code')
320
+ code = add_function_call(
321
+ code=code,
322
+ function_name=function_name,
323
+ parameters=[]
324
+ )
325
+ task.print_out(f'Write modified code to: {app_main_file_path}')
326
+ await write_text_file_async(app_main_file_path, code)