splent-cli 1.33.0__tar.gz → 1.33.1__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 (202) hide show
  1. {splent_cli-1.33.0/src/splent_cli.egg-info → splent_cli-1.33.1}/PKG-INFO +1 -1
  2. {splent_cli-1.33.0 → splent_cli-1.33.1}/pyproject.toml +1 -1
  3. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_test.py +59 -14
  4. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_restart.py +258 -5
  5. {splent_cli-1.33.0 → splent_cli-1.33.1/src/splent_cli.egg-info}/PKG-INFO +1 -1
  6. {splent_cli-1.33.0 → splent_cli-1.33.1}/LICENSE +0 -0
  7. {splent_cli-1.33.0 → splent_cli-1.33.1}/README.md +0 -0
  8. {splent_cli-1.33.0 → splent_cli-1.33.1}/setup.cfg +0 -0
  9. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/__init__.py +0 -0
  10. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/__main__.py +0 -0
  11. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/cli.py +0 -0
  12. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/__init__.py +0 -0
  13. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/__init__.py +0 -0
  14. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  15. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  16. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  17. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  18. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_size.py +0 -0
  19. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_status.py +0 -0
  20. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  21. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  22. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/__init__.py +0 -0
  23. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_contracts.py +0 -0
  24. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_deps.py +0 -0
  25. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_docker.py +0 -0
  26. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_env.py +0 -0
  27. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_features.py +0 -0
  28. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_github.py +0 -0
  29. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_infra.py +0 -0
  30. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_product.py +0 -0
  31. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_pypi.py +0 -0
  32. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  33. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/check/check_releases.py +0 -0
  34. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/clear/__init__.py +0 -0
  35. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/clear/clear_build.py +0 -0
  36. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/clear/clear_log.py +0 -0
  37. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  38. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/command/__init__.py +0 -0
  39. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/command/command_create.py +0 -0
  40. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/coverage.py +0 -0
  41. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_console.py +0 -0
  42. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_dump.py +0 -0
  43. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_migrate.py +0 -0
  44. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_reset.py +0 -0
  45. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_restore.py +0 -0
  46. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_rollback.py +0 -0
  47. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_seed.py +0 -0
  48. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_status.py +0 -0
  49. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  50. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/doctor.py +0 -0
  51. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/env/env_list.py +0 -0
  52. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/env/env_set.py +0 -0
  53. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/env/env_show.py +0 -0
  54. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/export/__init__.py +0 -0
  55. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/export/export_puml.py +0 -0
  56. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_add.py +0 -0
  57. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  58. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  59. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  60. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  61. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  62. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  63. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_create.py +0 -0
  64. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  65. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  66. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  67. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  68. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_env.py +0 -0
  69. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  70. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_git.py +0 -0
  71. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  72. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  73. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  74. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  75. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_info.py +0 -0
  76. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  77. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_install.py +0 -0
  78. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_list.py +0 -0
  79. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_order.py +0 -0
  80. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  81. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  82. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  83. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  84. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  85. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_release.py +0 -0
  86. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  87. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  88. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_search.py +0 -0
  89. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_status.py +0 -0
  90. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  91. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  92. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  93. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  94. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  95. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  96. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/lint.py +0 -0
  97. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/locust.py +0 -0
  98. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/marketplace/login.py +0 -0
  99. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/marketplace/logout.py +0 -0
  100. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  101. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
  102. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  103. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/__init__.py +0 -0
  104. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  105. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_build.py +0 -0
  106. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_clean.py +0 -0
  107. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_commands.py +0 -0
  108. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_config.py +0 -0
  109. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_configure.py +0 -0
  110. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_console.py +0 -0
  111. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_containers.py +0 -0
  112. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_create.py +0 -0
  113. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_deploy.py +0 -0
  114. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_derive.py +0 -0
  115. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_deselect.py +0 -0
  116. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_down.py +0 -0
  117. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_drift.py +0 -0
  118. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_env.py +0 -0
  119. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_list.py +0 -0
  120. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_logs.py +0 -0
  121. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_missing.py +0 -0
  122. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_port.py +0 -0
  123. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_release.py +0 -0
  124. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_resolve.py +0 -0
  125. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_routes.py +0 -0
  126. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_run.py +0 -0
  127. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_select.py +0 -0
  128. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_shell.py +0 -0
  129. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_signals.py +0 -0
  130. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  131. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_test.py +0 -0
  132. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_up.py +0 -0
  133. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/product/product_validate.py +0 -0
  134. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/release/__init__.py +0 -0
  135. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/release/release_core.py +0 -0
  136. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/release/release_resume.py +0 -0
  137. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/selenium.py +0 -0
  138. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/__init__.py +0 -0
  139. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  140. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  141. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  142. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  143. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_create.py +0 -0
  144. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  145. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_features.py +0 -0
  146. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  147. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_info.py +0 -0
  148. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_list.py +0 -0
  149. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  150. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  151. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  152. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  153. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  154. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/tokens_setup.py +0 -0
  155. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  156. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/commands/version.py +0 -0
  157. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/__init__.py +0 -0
  158. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/compose.py +0 -0
  159. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/context.py +0 -0
  160. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/credentials.py +0 -0
  161. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/deploy_bundle.py +0 -0
  162. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/image_registry.py +0 -0
  163. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/index_refresh.py +0 -0
  164. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/marketplace.py +0 -0
  165. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/marketplace_api.py +0 -0
  166. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/marketplace_url.py +0 -0
  167. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/preflight.py +0 -0
  168. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/pypi_quota.py +0 -0
  169. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/registry.py +0 -0
  170. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/release.py +0 -0
  171. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/release_dry_run.py +0 -0
  172. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/release_gate.py +0 -0
  173. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/services/spl_store.py +0 -0
  174. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/__init__.py +0 -0
  175. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/archetype.py +0 -0
  176. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/cache_utils.py +0 -0
  177. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/command_loader.py +0 -0
  178. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/contract_freshness.py +0 -0
  179. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/db_client.py +0 -0
  180. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/db_utils.py +0 -0
  181. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/decorators.py +0 -0
  182. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/dynamic_imports.py +0 -0
  183. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/feature_installer.py +0 -0
  184. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/feature_utils.py +0 -0
  185. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/forge.py +0 -0
  186. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/git_url.py +0 -0
  187. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/integrity.py +0 -0
  188. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/io_utils.py +0 -0
  189. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/lifecycle.py +0 -0
  190. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/manifest.py +0 -0
  191. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/path_utils.py +0 -0
  192. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/proc.py +0 -0
  193. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/redact.py +0 -0
  194. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/refinement_facts.py +0 -0
  195. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/template_drift.py +0 -0
  196. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/toml_edit.py +0 -0
  197. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli/utils/upgrade_plan.py +0 -0
  198. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli.egg-info/SOURCES.txt +0 -0
  199. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  200. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli.egg-info/entry_points.txt +0 -0
  201. {splent_cli-1.33.0 → splent_cli-1.33.1}/src/splent_cli.egg-info/requires.txt +0 -0
  202. {splent_cli-1.33.0 → splent_cli-1.33.1}/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.33.0
3
+ Version: 1.33.1
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.33.0"
7
+ version = "1.33.1"
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"
@@ -47,6 +47,42 @@ def _container_running(name: str) -> bool:
47
47
 
48
48
  _PASSTHROUGH_ENV = ("PYTEST_ADDOPTS", "PYTHONDONTWRITEBYTECODE")
49
49
 
50
+ # One test run at a time in a product's container: the runs share its test
51
+ # database, and two at once fail each other's tests.
52
+ TEST_LOCK = "/tmp/splent-feature-test.lock"
53
+
54
+
55
+ def _tied_to_the_cli(command: str, lock: str = TEST_LOCK) -> str:
56
+ """``command`` as a script that stops when the CLI that started it does.
57
+
58
+ A docker exec without a terminal forwards no signal, so a Ctrl-C, or a
59
+ release stopped halfway, used to kill only the CLI. The tests went on in
60
+ the container against the test database, and the next run, started
61
+ beside them, failed in ways the code under test had nothing to do with.
62
+
63
+ The CLI holds the exec's stdin open while it runs (see
64
+ _delegate_to_web_container). When the CLI goes, stdin closes, and the
65
+ watcher stops the whole run. A run that finds another one holding the
66
+ lock waits for it instead of running beside it.
67
+ """
68
+ return f"""set -m
69
+ exec 3<&0
70
+ if command -v flock >/dev/null 2>&1; then
71
+ exec 9>{lock}
72
+ if ! flock -n 9; then
73
+ echo " Another test run is using this product's test database. Waiting for it to finish..."
74
+ flock 9
75
+ fi
76
+ fi
77
+ ( {command} ) </dev/null &
78
+ run=$!
79
+ ( exec 9>&-; cat <&3 >/dev/null; kill -TERM -- -$run 2>/dev/null ) &
80
+ watch=$!
81
+ wait $run
82
+ rc=$?
83
+ kill -- -$watch 2>/dev/null
84
+ exit $rc"""
85
+
50
86
 
51
87
  def _delegate_to_web_container(argv: list[str]) -> None:
52
88
  """Re-run this command inside the product's web container, and exit.
@@ -80,20 +116,29 @@ def _delegate_to_web_container(argv: list[str]) -> None:
80
116
  value = os.environ.get(var)
81
117
  if value:
82
118
  passthrough += ["-e", f"{var}={value}"]
83
- result = run(
84
- [
85
- "docker",
86
- "exec",
87
- "-e",
88
- f"SPLENT_APP={context.require_app()}",
89
- *passthrough,
90
- name,
91
- "bash",
92
- "-lc",
93
- command,
94
- ],
95
- check=False,
96
- )
119
+ # The exec reads a pipe only this process writes to, so it sees the end
120
+ # of its input exactly when this process ends, however it ends.
121
+ read_end, write_end = os.pipe()
122
+ try:
123
+ result = run(
124
+ [
125
+ "docker",
126
+ "exec",
127
+ "-i",
128
+ "-e",
129
+ f"SPLENT_APP={context.require_app()}",
130
+ *passthrough,
131
+ name,
132
+ "bash",
133
+ "-lc",
134
+ _tied_to_the_cli(command),
135
+ ],
136
+ check=False,
137
+ stdin=read_end,
138
+ )
139
+ finally:
140
+ os.close(read_end)
141
+ os.close(write_end)
97
142
  raise SystemExit(result.returncode)
98
143
 
99
144
 
@@ -8,6 +8,7 @@ In production it restarts the web container, whose command is the entrypoint.
8
8
 
9
9
  import os
10
10
  import subprocess
11
+ import time
11
12
  import tomllib
12
13
 
13
14
  import click
@@ -179,6 +180,196 @@ KILL_DEV_SERVER = (
179
180
  )
180
181
 
181
182
 
183
+ # ── Waiting for the app ──────────────────────────────────────────────
184
+
185
+ # Where a development container writes what the app and the entrypoint say:
186
+ # the start script sends Flask's output to the first, and the entrypoint,
187
+ # run as root, tees its steps into the second.
188
+ DEV_LOGS = ("/var/log/entrypoint.log", "/var/log/splent/entrypoint.log")
189
+
190
+ # How long the app gets to answer after a restart. A full restart reinstalls
191
+ # the dependencies and compiles the assets first, and production runs its
192
+ # migrations before gunicorn binds (its healthcheck allows 180 s).
193
+ START_TIMEOUT = {"quick": 120, "full": 600, "prod": 180}
194
+ START_POLL = 1.0
195
+
196
+ # What only a start that failed writes. watchmedo starts Flask again every
197
+ # time it exits, so a broken app never stops, it only prints its traceback
198
+ # over and over. The entrypoint marks a failed step with FAIL.
199
+ START_FAILURES = (
200
+ "Traceback (most recent call last)",
201
+ "Error: While importing",
202
+ "Error: Could not import",
203
+ "Error: Could not locate",
204
+ "is in use by another program",
205
+ " FAIL ",
206
+ )
207
+
208
+ LOG_TAIL_LINES = 25
209
+
210
+
211
+ def _exec(container_id: str, script: str) -> subprocess.CompletedProcess:
212
+ return subprocess.run(
213
+ ["docker", "exec", container_id, "sh", "-c", script],
214
+ capture_output=True,
215
+ text=True,
216
+ )
217
+
218
+
219
+ def _log_sizes(container_id: str, paths=DEV_LOGS) -> dict[str, int]:
220
+ """Size of each log now, so only what the restart writes is read later."""
221
+ sizes = {}
222
+ for path in paths:
223
+ result = _exec(container_id, f"stat -c %s {path} 2>/dev/null || echo 0")
224
+ try:
225
+ sizes[path] = int(result.stdout.strip() or 0)
226
+ except ValueError:
227
+ sizes[path] = 0
228
+ return sizes
229
+
230
+
231
+ def _written_since(container_id: str, sizes: dict[str, int]) -> str:
232
+ """What the logs gained since ``sizes`` were taken."""
233
+ parts = []
234
+ for path, size in sizes.items():
235
+ result = _exec(
236
+ container_id,
237
+ f"[ -f {path} ] || exit 0; "
238
+ f'if [ "$(stat -c %s {path})" -lt {size} ]; then cat {path}; '
239
+ f"else tail -c +{size + 1} {path}; fi",
240
+ )
241
+ if result.stdout:
242
+ parts.append(result.stdout)
243
+ return "".join(parts)
244
+
245
+
246
+ def _http_status(container_id: str) -> int | None:
247
+ """Status the app answers on port 5000 inside its container, or None."""
248
+ result = _exec(
249
+ container_id,
250
+ "curl -s -o /dev/null -w '%{http_code}' --max-time 3 http://localhost:5000/",
251
+ )
252
+ try:
253
+ status = int(result.stdout.strip()[-3:])
254
+ except ValueError:
255
+ return None
256
+ return status or None
257
+
258
+
259
+ def _container_state(container_id: str) -> tuple[str, int, int]:
260
+ """(status, exit code, restart count) of the container."""
261
+ result = subprocess.run(
262
+ [
263
+ "docker",
264
+ "inspect",
265
+ "-f",
266
+ "{{.State.Status}} {{.State.ExitCode}} {{.RestartCount}}",
267
+ container_id,
268
+ ],
269
+ capture_output=True,
270
+ text=True,
271
+ )
272
+ fields = result.stdout.split()
273
+ if result.returncode != 0 or len(fields) != 3:
274
+ return "missing", 0, 0
275
+ return fields[0], int(fields[1]), int(fields[2])
276
+
277
+
278
+ def _tail(text: str, lines: int = LOG_TAIL_LINES) -> str:
279
+ return "\n".join(text.strip().splitlines()[-lines:])
280
+
281
+
282
+ def _last_failure(text: str) -> str:
283
+ """The end of the log from its last traceback on.
284
+
285
+ watchmedo starts the broken app again and again, so the log repeats the
286
+ same error; once is enough. Flask's own "Error: While importing" line,
287
+ just above the traceback, stays with it.
288
+ """
289
+ at = text.rfind("Traceback (most recent call last)")
290
+ if at < 0:
291
+ return _tail(text)
292
+ error = text.rfind("Error: ", 0, at)
293
+ if error >= 0 and text.count("\n", error, at) <= 3:
294
+ at = error
295
+ return _tail(text[text.rfind("\n", 0, at) + 1 :])
296
+
297
+
298
+ def wait_for_dev_start(container_id, sizes, timeout, poll=START_POLL):
299
+ """Wait for a development app to answer after a restart.
300
+
301
+ Returns ``(None, "")`` once it answers, any status included, since a 500
302
+ is the application's own error and the browser shows it. Otherwise
303
+ returns why it did not start and the end of what its logs said.
304
+ """
305
+ deadline = time.monotonic() + timeout
306
+ announced = False
307
+ while True:
308
+ if _http_status(container_id):
309
+ return None, ""
310
+ written = _written_since(container_id, sizes)
311
+ if any(marker in written for marker in START_FAILURES):
312
+ return "the app failed to start", _last_failure(written)
313
+ if time.monotonic() >= deadline:
314
+ return f"the app did not answer within {timeout} s", _tail(written)
315
+ if not announced:
316
+ click.echo(click.style(" waiting ", dim=True) + "for the app to answer...")
317
+ announced = True
318
+ time.sleep(poll)
319
+
320
+
321
+ def wait_for_prod_start(container_id, timeout, poll=START_POLL):
322
+ """Wait for a restarted production container to answer below 500.
323
+
324
+ The same bar as the image's healthcheck, which the autodeploy agent
325
+ rolls back on. Returns ``(None, "")`` once it is met, otherwise why not
326
+ and the end of the container's log.
327
+ """
328
+ _, _, restarts = _container_state(container_id)
329
+ deadline = time.monotonic() + timeout
330
+ announced = False
331
+ reason = None
332
+ while reason is None:
333
+ status, exit_code, count = _container_state(container_id)
334
+ if status in ("exited", "dead", "missing"):
335
+ reason = f"the container stopped (exit code {exit_code})"
336
+ break
337
+ if count > restarts:
338
+ reason = "the container keeps restarting"
339
+ break
340
+ code = _http_status(container_id) if status == "running" else None
341
+ if code and code < 500:
342
+ return None, ""
343
+ if time.monotonic() >= deadline:
344
+ reason = (
345
+ f"the app answers HTTP {code}"
346
+ if code
347
+ else f"the app did not answer within {timeout} s"
348
+ )
349
+ break
350
+ if not announced:
351
+ click.echo(click.style(" waiting ", dim=True) + "for the app to answer...")
352
+ announced = True
353
+ time.sleep(poll)
354
+ logs = subprocess.run(
355
+ ["docker", "logs", "--tail", str(LOG_TAIL_LINES), container_id],
356
+ capture_output=True,
357
+ text=True,
358
+ )
359
+ return reason, _tail((logs.stdout or "") + (logs.stderr or ""))
360
+
361
+
362
+ def _report_start_failure(product, env, reason, log_tail, log_hint):
363
+ click.secho(f" {product} ({env}) did not start, {reason}.", fg="red")
364
+ if log_tail:
365
+ click.echo()
366
+ for line in log_tail.splitlines():
367
+ click.echo(click.style(" ", dim=True) + line)
368
+ click.echo()
369
+ click.echo(f" See the whole log with {log_hint}")
370
+ raise SystemExit(1)
371
+
372
+
182
373
  # ── Command ──────────────────────────────────────────────────────────
183
374
 
184
375
 
@@ -257,9 +448,33 @@ def product_restart(env_dev, env_prod, full):
257
448
  )
258
449
  raise SystemExit(1)
259
450
 
451
+ # ── The env file the start reads ──────────────────────────────
452
+ # The start sources docker/.env with bash first, in a detached exec whose
453
+ # errors nobody sees. Read it now, before the running app is stopped, so
454
+ # a line bash cannot read is reported here and the app keeps running.
455
+ env_check = (
456
+ None
457
+ if env == "prod"
458
+ else _exec(
459
+ container_id, f"bash -c 'set -a && . /workspace/{product}/docker/.env'"
460
+ )
461
+ )
462
+ if env_check is not None and env_check.returncode != 0:
463
+ detail = (env_check.stderr or env_check.stdout or "").strip()
464
+ click.secho(
465
+ f" {product}/docker/.env cannot be read by bash, so the app would "
466
+ "not start. Nothing was restarted.",
467
+ fg="red",
468
+ )
469
+ if detail:
470
+ click.secho(f" {_tail(detail, 5)}", fg="red")
471
+ click.echo(f" Write it again with splent product:env --merge --{env}")
472
+ raise SystemExit(1)
473
+
260
474
  # ── Restart feature Docker containers (nginx, redis, etc.) ──
261
475
  features = _get_feature_order(workspace, product_path, env)
262
476
  restarted = 0
477
+ services_failed: list[str] = []
263
478
  for feat in features:
264
479
  clean = compose.normalize_feature_ref(feat)
265
480
  feat_docker = compose.feature_docker_dir(workspace, clean)
@@ -294,6 +509,10 @@ def product_restart(env_dev, env_prod, full):
294
509
  restarted += 1
295
510
  else:
296
511
  click.echo(click.style(" ✗", fg="red"))
512
+ detail = (result.stderr or result.stdout or "").strip()
513
+ if detail:
514
+ click.secho(f" {_tail(detail, 3)}", fg="red")
515
+ services_failed.append(short)
297
516
 
298
517
  # ── Production: restart the web container ─────────────────────
299
518
  # Its command is the entrypoint, which ends by running gunicorn in the
@@ -314,7 +533,16 @@ def product_restart(env_dev, env_prod, full):
314
533
  if detail:
315
534
  click.secho(f" {detail}", fg="red")
316
535
  raise SystemExit(1)
317
- click.secho(" done.", fg="green")
536
+ reason, log_tail = wait_for_prod_start(container_id, START_TIMEOUT["prod"])
537
+ if reason:
538
+ _report_start_failure(
539
+ product,
540
+ env,
541
+ reason,
542
+ log_tail,
543
+ f"docker logs {container_id[:12]}",
544
+ )
545
+ _finish(services_failed)
318
546
  return
319
547
 
320
548
  # ── Kill existing processes ───────────────────────────────────
@@ -322,6 +550,8 @@ def product_restart(env_dev, env_prod, full):
322
550
  ["docker", "exec", container_id, "bash", "-c", KILL_DEV_SERVER],
323
551
  capture_output=True,
324
552
  )
553
+ # What the logs hold now, so a failure is told from what this start writes.
554
+ sizes = _log_sizes(container_id)
325
555
 
326
556
  if full:
327
557
  click.echo(
@@ -335,10 +565,9 @@ def product_restart(env_dev, env_prod, full):
335
565
  start_script = _start_script(product, env)
336
566
  source_cmd = f"set -a && . /workspace/{product}/docker/.env && set +a && bash {start_script}"
337
567
 
338
- # ``-d`` detaches, so this returncode reflects whether the app could be
339
- # *launched* in the container (container reachable, exec accepted), not the
340
- # eventual health of the app. A non-zero exit here means the start never
341
- # happened, so don't claim success.
568
+ # ``-d`` detaches, so this returncode only says whether the start could be
569
+ # launched in the container. Whether the app came up is what the wait
570
+ # below finds out.
342
571
  result = run(
343
572
  ["docker", "exec", "-d", container_id, "bash", "-c", source_cmd],
344
573
  check=False,
@@ -351,6 +580,30 @@ def product_restart(env_dev, env_prod, full):
351
580
  click.secho(f" {detail}", fg="red")
352
581
  raise SystemExit(1)
353
582
 
583
+ reason, log_tail = wait_for_dev_start(
584
+ container_id, sizes, START_TIMEOUT["full" if full else "quick"]
585
+ )
586
+ if reason:
587
+ log_path = DEV_LOGS[1] if full else DEV_LOGS[0]
588
+ _report_start_failure(
589
+ product,
590
+ env,
591
+ reason,
592
+ log_tail,
593
+ f"docker exec {container_id[:12]} tail -n 100 {log_path}",
594
+ )
595
+ _finish(services_failed)
596
+
597
+
598
+ def _finish(services_failed: list[str]) -> None:
599
+ """Say done only when everything restarted."""
600
+ if services_failed:
601
+ click.secho(
602
+ f" The app is up, but {len(services_failed)} feature service(s) did "
603
+ f"not restart: {', '.join(services_failed)}.",
604
+ fg="red",
605
+ )
606
+ raise SystemExit(1)
354
607
  click.secho(" done.", fg="green")
355
608
 
356
609
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.33.0
3
+ Version: 1.33.1
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
File without changes
File without changes
File without changes