splent-cli 1.12.0__tar.gz → 1.13.0__tar.gz

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 (171) hide show
  1. {splent_cli-1.12.0/src/splent_cli.egg-info → splent_cli-1.13.0}/PKG-INFO +1 -1
  2. {splent_cli-1.12.0 → splent_cli-1.13.0}/pyproject.toml +1 -1
  3. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_reset.py +24 -10
  4. splent_cli-1.13.0/src/splent_cli/commands/locust.py +140 -0
  5. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/dynamic_imports.py +10 -0
  6. {splent_cli-1.12.0 → splent_cli-1.13.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
  7. splent_cli-1.12.0/src/splent_cli/commands/locust.py +0 -271
  8. {splent_cli-1.12.0 → splent_cli-1.13.0}/LICENSE +0 -0
  9. {splent_cli-1.12.0 → splent_cli-1.13.0}/README.md +0 -0
  10. {splent_cli-1.12.0 → splent_cli-1.13.0}/setup.cfg +0 -0
  11. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/__init__.py +0 -0
  12. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/__main__.py +0 -0
  13. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/cli.py +0 -0
  14. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/__init__.py +0 -0
  15. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  16. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  17. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  18. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  19. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  20. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  21. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  22. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  23. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  24. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/__init__.py +0 -0
  25. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  26. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  27. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_env.py +0 -0
  28. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_features.py +0 -0
  29. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_github.py +0 -0
  30. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_infra.py +0 -0
  31. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_product.py +0 -0
  32. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  33. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  34. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  35. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  36. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  37. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  38. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/command/__init__.py +0 -0
  39. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/command/command_create.py +0 -0
  40. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/coverage.py +0 -0
  41. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_console.py +0 -0
  42. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  43. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
  44. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  45. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  46. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  47. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_status.py +0 -0
  48. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  49. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/doctor.py +0 -0
  50. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/env/env_list.py +0 -0
  51. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/env/env_set.py +0 -0
  52. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/env/env_show.py +0 -0
  53. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/export/__init__.py +0 -0
  54. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/export/export_puml.py +0 -0
  55. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  56. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  57. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  58. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  59. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  60. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  61. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  62. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  63. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  64. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  65. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  66. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  67. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  68. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  69. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  70. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  71. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  72. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  73. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  74. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  75. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
  76. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  77. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  78. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  79. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  80. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  81. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  82. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  83. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_release.py +0 -0
  84. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  85. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  86. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  87. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  88. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  89. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
  90. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  91. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  92. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  93. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  94. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  95. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/lint.py +0 -0
  96. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/__init__.py +0 -0
  97. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  98. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_build.py +0 -0
  99. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  100. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_commands.py +0 -0
  101. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_config.py +0 -0
  102. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_configure.py +0 -0
  103. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_console.py +0 -0
  104. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_containers.py +0 -0
  105. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_create.py +0 -0
  106. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
  107. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_derive.py +0 -0
  108. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  109. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_down.py +0 -0
  110. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  111. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_env.py +0 -0
  112. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_list.py +0 -0
  113. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  114. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  115. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_port.py +0 -0
  116. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_release.py +0 -0
  117. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  118. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  119. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  120. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_run.py +0 -0
  121. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_select.py +0 -0
  122. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_shell.py +0 -0
  123. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  124. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  125. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_test.py +0 -0
  126. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_up.py +0 -0
  127. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/product/product_validate.py +0 -0
  128. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/release/__init__.py +0 -0
  129. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/release/release_core.py +0 -0
  130. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/selenium.py +0 -0
  131. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  132. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  133. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  134. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  135. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  136. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  137. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  138. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  139. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  140. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  141. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  142. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  143. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/tokens_setup.py +0 -0
  144. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  145. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/commands/version.py +0 -0
  146. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/services/__init__.py +0 -0
  147. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/services/compose.py +0 -0
  148. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/services/context.py +0 -0
  149. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/services/preflight.py +0 -0
  150. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/services/release.py +0 -0
  151. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/__init__.py +0 -0
  152. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/cache_utils.py +0 -0
  153. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/command_loader.py +0 -0
  154. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  155. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/db_utils.py +0 -0
  156. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/decorators.py +0 -0
  157. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/feature_installer.py +0 -0
  158. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/feature_utils.py +0 -0
  159. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/git_url.py +0 -0
  160. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/integrity.py +0 -0
  161. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/io_utils.py +0 -0
  162. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/lifecycle.py +0 -0
  163. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/manifest.py +0 -0
  164. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/path_utils.py +0 -0
  165. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/proc.py +0 -0
  166. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli/utils/template_drift.py +0 -0
  167. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli.egg-info/SOURCES.txt +0 -0
  168. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  169. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  170. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli.egg-info/requires.txt +0 -0
  171. {splent_cli-1.12.0 → splent_cli-1.13.0}/src/splent_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.12.0"
7
+ version = "1.13.0"
8
8
  description = "SPLENT-CLI is a CLI to be able to work on your development more easily."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -220,18 +220,32 @@ def _reset_everything(app, yes, product_path, product_name):
220
220
  raise SystemExit(1)
221
221
 
222
222
  click.echo(click.style("📋 Recreating splent_migrations table...", fg="cyan"))
223
- with db.engine.begin() as conn:
224
- conn.execute(
225
- text(
226
- f"""
227
- CREATE TABLE IF NOT EXISTS `{SPLENT_MIGRATIONS_TABLE}` (
228
- `feature` VARCHAR(255) NOT NULL,
229
- `last_migration` VARCHAR(255) DEFAULT NULL,
230
- PRIMARY KEY (`feature`)
231
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
232
- """
223
+ try:
224
+ with db.engine.begin() as conn:
225
+ conn.execute(
226
+ text(
227
+ f"""
228
+ CREATE TABLE IF NOT EXISTS `{SPLENT_MIGRATIONS_TABLE}` (
229
+ `feature` VARCHAR(255) NOT NULL,
230
+ `last_migration` VARCHAR(255) DEFAULT NULL,
231
+ PRIMARY KEY (`feature`)
232
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
233
+ """
234
+ )
235
+ )
236
+ except Exception as exc:
237
+ # The drop above succeeded, so the schema is empty but the migration
238
+ # tracking table is gone: surface the partial state instead of a raw
239
+ # traceback, and tell the operator how to converge.
240
+ click.echo(
241
+ click.style(
242
+ f"Failed to recreate the migration tracking table `{SPLENT_MIGRATIONS_TABLE}`: {exc}\n"
243
+ "The database is empty but untracked. Re-run 'splent db:reset' once the "
244
+ "database connection is back.",
245
+ fg="red",
233
246
  )
234
247
  )
248
+ raise SystemExit(1)
235
249
 
236
250
  ctx = click.get_current_context()
237
251
  ctx.invoke(clear_uploads)
@@ -0,0 +1,140 @@
1
+ """splent locust — load testing for the active product.
2
+
3
+ Locust runs inside the product's web container, which already has
4
+ splent_framework and every installed feature importable, so no extra image is
5
+ built. Discovery is done by splent_framework's locustfile bootstrap (1.7.1+),
6
+ which finds each feature's locustfile on its own; a feature argument narrows it
7
+ through SPLENT_LOCUSTFILES.
8
+
9
+ The web interface listens on port 8089 inside the container. Products
10
+ scaffolded by this CLI publish that port; for an older product add
11
+ "8089:8089" to the web service's ports and recreate the container.
12
+ """
13
+
14
+ import os
15
+ import shlex
16
+
17
+ import click
18
+
19
+ from splent_cli.services import context
20
+ from splent_cli.utils.proc import require_docker, run
21
+
22
+ # Feature locustfile locations, relative to the workspace. Kept in sync with
23
+ # splent_framework.bootstraps.locustfile_bootstrap.DEFAULT_PATTERNS.
24
+ FEATURE_LOCUSTFILE_PATTERNS = (
25
+ "features/*/{feature}/tests/load/locustfile.py",
26
+ "features/*/{feature}/tests/locustfile.py",
27
+ "features/*/{feature}/src/*/*/tests/load/locustfile.py",
28
+ "app/features/{feature}/tests/locustfile.py",
29
+ )
30
+
31
+ # Resolved inside the container: the framework bootstrap that discovers and
32
+ # re-exports every feature's HttpUser classes.
33
+ BOOTSTRAP_SNIPPET = (
34
+ "locust -f \"$(python -c 'import splent_framework.bootstraps.locustfile_bootstrap as m; "
35
+ "print(m.__file__)')\" --web-host 0.0.0.0"
36
+ )
37
+
38
+
39
+ def _web_container() -> str:
40
+ return f"{context.require_app()}_web"
41
+
42
+
43
+ def _container_running(name: str) -> bool:
44
+ result = run(
45
+ ["docker", "ps", "-q", "-f", f"name=^{name}$"], check=False, capture=True
46
+ )
47
+ return result.returncode == 0 and bool(result.stdout.strip())
48
+
49
+
50
+ def _locust_installed(name: str) -> bool:
51
+ result = run(
52
+ ["docker", "exec", name, "python", "-c", "import locust"],
53
+ check=False,
54
+ capture=True,
55
+ )
56
+ return result.returncode == 0
57
+
58
+
59
+ def _feature_patterns(feature: str) -> str:
60
+ return ",".join(p.format(feature=feature) for p in FEATURE_LOCUSTFILE_PATTERNS)
61
+
62
+
63
+ def _feature_has_locustfile(workspace: str, feature: str) -> bool:
64
+ import glob
65
+
66
+ for pattern in FEATURE_LOCUSTFILE_PATTERNS:
67
+ if glob.glob(os.path.join(workspace, pattern.format(feature=feature))):
68
+ return True
69
+ return False
70
+
71
+
72
+ @click.command(
73
+ "locust", short_help="Run Locust load tests inside the product web container."
74
+ )
75
+ @click.argument("feature", required=False)
76
+ @context.requires_product
77
+ def locust(feature):
78
+ require_docker()
79
+ container = _web_container()
80
+
81
+ if not _container_running(container):
82
+ raise click.ClickException(
83
+ f"Container '{container}' is not running. Start the product first: splent product:up"
84
+ )
85
+
86
+ if not _locust_installed(container):
87
+ raise click.ClickException(
88
+ f"locust is not installed in '{container}'.\n"
89
+ 'Add "locust" to [project.optional-dependencies].dev in the product\'s '
90
+ "pyproject.toml and rebuild (splent product:derive), or install it once "
91
+ f"with: docker exec {container} pip install locust"
92
+ )
93
+
94
+ workspace = str(context.workspace())
95
+ env_args = ["-e", "WORKING_DIR=/workspace"]
96
+ if feature:
97
+ if not _feature_has_locustfile(workspace, feature):
98
+ raise click.ClickException(
99
+ f"No locustfile found for feature '{feature}'. Looked under: "
100
+ + ", ".join(FEATURE_LOCUSTFILE_PATTERNS)
101
+ )
102
+ env_args += ["-e", f"SPLENT_LOCUSTFILES={_feature_patterns(feature)}"]
103
+
104
+ check = run(
105
+ ["docker", "exec", container, "sh", "-c", "pgrep -f 'locust' >/dev/null"],
106
+ check=False,
107
+ capture=True,
108
+ )
109
+ if check.returncode == 0:
110
+ click.echo("Locust is already running in the product container.")
111
+ return
112
+
113
+ cmd = ["docker", "exec", "-d", *env_args, container, "sh", "-c", BOOTSTRAP_SNIPPET]
114
+ click.echo(f"Command: {' '.join(shlex.quote(c) for c in cmd)}")
115
+ run(cmd)
116
+ click.echo(click.style("Locust is running at http://localhost:8089", fg="green"))
117
+ click.echo("Stop it with: splent locust:stop")
118
+
119
+
120
+ @click.command(
121
+ "locust:stop", short_help="Stop the Locust process in the product web container."
122
+ )
123
+ @context.requires_product
124
+ def locust_stop():
125
+ require_docker()
126
+ container = _web_container()
127
+
128
+ if not _container_running(container):
129
+ click.echo(f"Container '{container}' is not running; nothing to stop.")
130
+ return
131
+
132
+ result = run(
133
+ ["docker", "exec", container, "sh", "-c", "pkill -f 'locust' || true"],
134
+ check=False,
135
+ capture=True,
136
+ )
137
+ if result.returncode == 0:
138
+ click.echo("Locust stopped.")
139
+ else:
140
+ click.echo("No Locust process was running.")
@@ -10,6 +10,10 @@ from splent_framework.utils.path_utils import PathUtils
10
10
  from splent_cli.utils.io_utils import load_toml
11
11
  from splent_cli.utils.proc import run
12
12
 
13
+ # What the operator actually exported, captured before any .env is applied.
14
+ # Precedence from here on: shell environment > product .env > workspace .env.
15
+ _shell_env = dict(os.environ)
16
+
13
17
  load_dotenv()
14
18
 
15
19
  _app_instance = None
@@ -21,7 +25,13 @@ dotenv_path = None
21
25
  if module_name:
22
26
  dotenv_path = PathUtils.get_app_env_file()
23
27
  if os.path.exists(dotenv_path):
28
+ # The product's .env beats the workspace .env, hence override=True.
29
+ # But it must never beat the process environment: a product .env
30
+ # carries container paths (WORKING_DIR=/workspace/), and blindly
31
+ # overriding made the CLI unusable from the host, because whatever
32
+ # WORKING_DIR the operator exported was clobbered on import.
24
33
  load_dotenv(dotenv_path, override=True)
34
+ os.environ.update(_shell_env)
25
35
 
26
36
 
27
37
  def install_features_if_needed():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
@@ -1,271 +0,0 @@
1
- import os
2
- import subprocess
3
- import click
4
- import docker
5
- import signal
6
- import psutil
7
-
8
- from splent_cli.services import context
9
- from splent_cli.utils.proc import run, require_docker, require_tool
10
-
11
-
12
- @click.command("locust", short_help="Launch the Locust load testing container.")
13
- @click.argument("module", required=False)
14
- @context.requires_product
15
- def locust(module):
16
- # Absolute paths
17
- working_dir = os.getenv("WORKING_DIR", "")
18
- core_dir = os.path.join(working_dir, "core")
19
- docker_dir = os.path.join(working_dir, "docker/")
20
- modules_dir = os.path.join(working_dir, "app/modules")
21
-
22
- def validate_module(module):
23
- """Check if the module exists."""
24
- if module:
25
- module_path = os.path.join(modules_dir, module)
26
- if not os.path.exists(module_path):
27
- raise click.UsageError(f"module '{module}' does not exist.")
28
- locustfile_path = os.path.join(module_path, "tests", "locustfile.py")
29
- if not os.path.exists(locustfile_path):
30
- raise click.UsageError(
31
- f"Locustfile for module '{module}' does not exist at path "
32
- f"'{locustfile_path}'."
33
- )
34
-
35
- def run_docker_locust(volume_name, module):
36
- """Build and run the Locust container with the specified volume."""
37
-
38
- try:
39
- # Check if the container already exists
40
- client.containers.get("locust_container")
41
- click.echo("Locust container is already running.")
42
- return
43
- except docker.errors.NotFound:
44
- pass # Container does not exist, proceed to create it
45
-
46
- click.echo(
47
- f"Starting Locust in Docker environment on port 8089 with volume: {volume_name}..."
48
- )
49
-
50
- # Build Locust's image
51
- build_command = [
52
- "docker",
53
- "build",
54
- "-f",
55
- os.path.join(docker_dir, "images/Dockerfile.locust"),
56
- "-t",
57
- "locust-image",
58
- ".",
59
- ]
60
- click.echo(f"Build command: {' '.join(build_command)}")
61
- run(build_command)
62
-
63
- # Define the locustfile path
64
- locustfile_path = os.path.join(core_dir, "bootstraps/locustfile_bootstrap.py")
65
- if module:
66
- locustfile_path = f"{modules_dir}/{module}/tests/locustfile.py"
67
-
68
- # Only attach to docker_flasky_network if it actually exists; otherwise
69
- # the container would fail to start with an unhelpful docker error.
70
- network_name = "docker_flasky_network"
71
- network_args = []
72
- network_check = run(
73
- ["docker", "network", "inspect", network_name],
74
- check=False,
75
- capture=True,
76
- )
77
- if network_check.returncode == 0:
78
- network_args = ["--network", network_name]
79
- else:
80
- click.secho(
81
- f"⚠️ Network '{network_name}' not found; starting Locust without it.",
82
- fg="yellow",
83
- )
84
-
85
- # Run the Locust container
86
- up_command = [
87
- "docker",
88
- "run",
89
- "-d",
90
- "-p",
91
- "8089:8089",
92
- "-v",
93
- f"{volume_name}:/workspace",
94
- "--name",
95
- "locust_container",
96
- *network_args,
97
- "locust-image",
98
- "-f",
99
- locustfile_path,
100
- ]
101
-
102
- click.echo(f"Docker Run command: {' '.join(up_command)}")
103
- run(up_command)
104
- click.echo(
105
- click.style("Locust is running at http://localhost:8089", fg="green")
106
- )
107
-
108
- def _is_our_locust(proc):
109
- """True only for a locust process started from this working dir."""
110
- try:
111
- info = proc.as_dict(attrs=["name", "cmdline", "cwd"])
112
- except (psutil.NoSuchProcess, psutil.AccessDenied):
113
- return False
114
- name = info.get("name") or ""
115
- cmdline = info.get("cmdline") or []
116
- is_locust = name == "locust" or any(
117
- os.path.basename(part) == "locust" for part in cmdline[:1]
118
- )
119
- if not is_locust:
120
- return False
121
- # Scope to processes launched from this working dir so we don't match
122
- # an unrelated locust running elsewhere on the machine.
123
- cwd = info.get("cwd")
124
- if cwd and working_dir:
125
- return os.path.normpath(cwd).startswith(os.path.normpath(working_dir))
126
- return cwd is None or not working_dir
127
-
128
- def is_locust_running():
129
- """Check if Locust is already running for this working dir."""
130
- for proc in psutil.process_iter(["pid", "name"]):
131
- if _is_our_locust(proc):
132
- return True
133
- return False
134
-
135
- def run_in_console(module):
136
- if is_locust_running():
137
- click.echo("Locust is already running.")
138
- return
139
-
140
- locustfile_path = os.path.join(core_dir, "bootstraps/locustfile_bootstrap.py")
141
- if module:
142
- locustfile_path = os.path.join(
143
- modules_dir, module, "tests", "locustfile.py"
144
- )
145
- require_tool(
146
- "locust",
147
- "Install it with: pip install locust",
148
- )
149
- locust_command = ["locust", "-f", locustfile_path]
150
- click.echo(f"Locust command: {' '.join(locust_command)}")
151
- subprocess.Popen(
152
- locust_command,
153
- stdin=subprocess.PIPE,
154
- stdout=subprocess.DEVNULL,
155
- stderr=subprocess.DEVNULL,
156
- )
157
- click.echo(
158
- click.style("Locust is running at http://localhost:8089", fg="green")
159
- )
160
-
161
- def run_local_locust(module):
162
- """Run Locust in the local environment."""
163
- click.echo("Starting Locust in local environment on port 8089...")
164
- run_in_console(module)
165
-
166
- def run_vagrant_locust(module):
167
- """Run Locust in the Vagrant environment."""
168
- click.echo("Starting Locust in Vagrant environment on port 8089...")
169
- run_in_console(module)
170
-
171
- # Validate module if provided
172
- if module:
173
- validate_module(module)
174
-
175
- if working_dir == "/workspace/":
176
- # Ensure docker is installed and the daemon is reachable before we try
177
- # to talk to it, so a broken setup yields an actionable message.
178
- require_docker()
179
- try:
180
- client = docker.from_env()
181
- except docker.errors.DockerException as e:
182
- raise click.ClickException(f"Could not connect to the Docker daemon: {e}")
183
-
184
- try:
185
- web_container = client.containers.get("web_app_container")
186
- volume_name = next(
187
- (
188
- mount.get("Name") or mount.get("Source")
189
- for mount in web_container.attrs.get("Mounts", [])
190
- if mount.get("Destination") == "/app"
191
- ),
192
- None,
193
- )
194
-
195
- if not volume_name:
196
- raise ValueError("No volume or bind mount found mounted on /app")
197
-
198
- run_docker_locust(volume_name, module)
199
-
200
- except docker.errors.NotFound:
201
- click.echo(click.style("Web container not found.", fg="red"))
202
- except click.ClickException:
203
- raise
204
- except Exception as e:
205
- click.echo(click.style(f"An error occurred: {str(e)}", fg="red"))
206
-
207
- elif working_dir == "":
208
- run_local_locust(module)
209
-
210
- elif working_dir == "/vagrant/":
211
- run_vagrant_locust(module)
212
-
213
- else:
214
- click.echo(click.style(f"Unrecognized WORKING_DIR: {working_dir}", fg="red"))
215
-
216
-
217
- @click.command("locust:stop", short_help="Stop the Locust load testing container.")
218
- def stop():
219
- working_dir = os.getenv("WORKING_DIR", "")
220
-
221
- def _is_our_locust(proc):
222
- """True only for a locust process started from this working dir."""
223
- try:
224
- info = proc.as_dict(attrs=["name", "cmdline", "cwd"])
225
- except (psutil.NoSuchProcess, psutil.AccessDenied):
226
- return False
227
- name = info.get("name") or ""
228
- cmdline = info.get("cmdline") or []
229
- is_locust = name == "locust" or any(
230
- os.path.basename(part) == "locust" for part in cmdline[:1]
231
- )
232
- if not is_locust:
233
- return False
234
- cwd = info.get("cwd")
235
- if cwd and working_dir:
236
- return os.path.normpath(cwd).startswith(os.path.normpath(working_dir))
237
- return cwd is None or not working_dir
238
-
239
- def stop_local_locust():
240
- """Stop Locust process in the local environment."""
241
- click.echo("Stopping Locust in local environment...")
242
- for proc in psutil.process_iter(["pid", "name"]):
243
- if _is_our_locust(proc):
244
- click.echo(f"Stopping Locust process with PID {proc.pid}...")
245
- try:
246
- os.kill(proc.pid, signal.SIGTERM)
247
- except (ProcessLookupError, PermissionError) as e:
248
- click.secho(f"⚠️ Could not stop PID {proc.pid}: {e}", fg="yellow")
249
-
250
- def stop_docker_locust():
251
- click.echo("Stopping Locust container if it is running...")
252
- require_docker()
253
- stop_command = ["docker", "stop", "locust_container"]
254
- rm_command = ["docker", "rm", "locust_container"]
255
-
256
- result = run(stop_command, check=False, capture=True)
257
- if result.returncode != 0:
258
- click.secho("⚠️ Could not stop Locust container.", fg="yellow")
259
-
260
- result = run(rm_command, check=False, capture=True)
261
- if result.returncode != 0:
262
- click.secho("⚠️ Could not remove Locust container.", fg="yellow")
263
-
264
- if working_dir == "/workspace/":
265
- stop_docker_locust()
266
-
267
- elif working_dir == "" or working_dir == "/vagrant/":
268
- stop_local_locust()
269
-
270
- else:
271
- click.echo(click.style(f"Unrecognized WORKING_DIR: {working_dir}", fg="red"))
File without changes
File without changes
File without changes