splent-cli 1.48.2__tar.gz → 1.50.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 (324) hide show
  1. {splent_cli-1.48.2/src/splent_cli.egg-info → splent_cli-1.50.0}/PKG-INFO +2 -2
  2. {splent_cli-1.48.2 → splent_cli-1.50.0}/pyproject.toml +2 -21
  3. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/cli.py +41 -1
  4. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_clear.py +2 -2
  5. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_infra.py +1 -1
  6. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/clear/clear_uploads.py +16 -3
  7. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_compat.py +1 -1
  8. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_compile.py +28 -0
  9. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_contract.py +21 -1
  10. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_install.py +1 -1
  11. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_release.py +439 -38
  12. splent_cli-1.50.0/src/splent_cli/commands/feature/feature_security.py +114 -0
  13. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_test.py +1 -1
  14. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_translate.py +51 -12
  15. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_unlock.py +12 -2
  16. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_xray.py +1 -1
  17. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/lint.py +3 -2
  18. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_build.py +14 -26
  19. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_config.py +1 -1
  20. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_deploy.py +61 -48
  21. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_env.py +6 -0
  22. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_restart.py +44 -15
  23. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_routes.py +1 -1
  24. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_test.py +1 -1
  25. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_up.py +6 -0
  26. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_validate.py +112 -11
  27. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_configurations.py +20 -13
  28. splent_cli-1.50.0/src/splent_cli/commands/storage/storage_migrate.py +42 -0
  29. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/image_registry.py +2 -2
  30. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/neutral_app.py +174 -43
  31. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/release_dry_run.py +49 -0
  32. splent_cli-1.50.0/src/splent_cli/services/security_scan.py +1109 -0
  33. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/spl_store.py +0 -4
  34. splent_cli-1.50.0/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +61 -0
  35. splent_cli-1.50.0/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +61 -0
  36. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_.env.prod.example.j2 +3 -0
  37. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +3 -2
  38. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +8 -0
  39. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +4 -1
  40. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_400.html +0 -2
  41. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_401.html +0 -2
  42. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_404.html +0 -2
  43. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_500.html +0 -2
  44. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_base_template.html +17 -5
  45. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +8 -3
  46. splent_cli-1.50.0/src/splent_cli/templates/product/product_pyproject.toml.j2 +69 -0
  47. splent_cli-1.50.0/src/splent_cli/utils/__init__.py +0 -0
  48. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/dynamic_imports.py +69 -1
  49. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_utils.py +10 -5
  50. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/frontend_check.py +17 -3
  51. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/path_utils.py +8 -11
  52. {splent_cli-1.48.2 → splent_cli-1.50.0/src/splent_cli.egg-info}/PKG-INFO +2 -2
  53. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli.egg-info/SOURCES.txt +4 -0
  54. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli.egg-info/requires.txt +1 -1
  55. splent_cli-1.48.2/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -100
  56. splent_cli-1.48.2/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -86
  57. splent_cli-1.48.2/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -93
  58. {splent_cli-1.48.2 → splent_cli-1.50.0}/COPYING +0 -0
  59. {splent_cli-1.48.2 → splent_cli-1.50.0}/COPYING.LESSER +0 -0
  60. {splent_cli-1.48.2 → splent_cli-1.50.0}/README.md +0 -0
  61. {splent_cli-1.48.2 → splent_cli-1.50.0}/setup.cfg +0 -0
  62. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/__init__.py +0 -0
  63. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/__main__.py +0 -0
  64. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/__init__.py +0 -0
  65. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  66. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  67. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  68. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  69. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  70. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  71. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  72. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  73. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/__init__.py +0 -0
  74. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
  75. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  76. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  77. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_env.py +0 -0
  78. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_features.py +0 -0
  79. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_github.py +0 -0
  80. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_product.py +0 -0
  81. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  82. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  83. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_releases.py +0 -0
  84. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/check/check_vendor.py +0 -0
  85. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  86. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  87. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  88. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/command/__init__.py +0 -0
  89. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/command/command_create.py +0 -0
  90. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/coverage.py +0 -0
  91. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_console.py +0 -0
  92. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  93. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
  94. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_reset.py +0 -0
  95. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  96. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  97. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  98. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_status.py +0 -0
  99. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  100. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/doctor.py +0 -0
  101. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/env/env_list.py +0 -0
  102. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/env/env_set.py +0 -0
  103. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/env/env_show.py +0 -0
  104. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/export/__init__.py +0 -0
  105. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/export/export_puml.py +0 -0
  106. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  107. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  108. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  109. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  110. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  111. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  112. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  113. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  114. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  115. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  116. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  117. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_frontend_init.py +0 -0
  118. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  119. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  120. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  121. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  122. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  123. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
  124. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  125. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  126. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_npm.py +0 -0
  127. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  128. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  129. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  130. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  131. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  132. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  133. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  134. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  135. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  136. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  137. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  138. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  139. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  140. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/locust.py +0 -0
  141. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/marketplace/login.py +0 -0
  142. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
  143. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  144. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
  145. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  146. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/__init__.py +0 -0
  147. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  148. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  149. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_commands.py +0 -0
  150. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_configure.py +0 -0
  151. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_console.py +0 -0
  152. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_containers.py +0 -0
  153. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_create.py +0 -0
  154. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_derive.py +0 -0
  155. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  156. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_down.py +0 -0
  157. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  158. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_frontend_audit.py +0 -0
  159. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_list.py +0 -0
  160. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  161. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  162. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_params.py +0 -0
  163. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_port.py +0 -0
  164. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_release.py +0 -0
  165. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  166. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_run.py +0 -0
  167. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_select.py +0 -0
  168. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_shell.py +0 -0
  169. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  170. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  171. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/release/__init__.py +0 -0
  172. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/release/release_core.py +0 -0
  173. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/release/release_resume.py +0 -0
  174. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/selenium.py +0 -0
  175. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  176. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  177. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  178. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  179. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  180. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  181. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  182. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  183. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  184. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  185. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  186. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  187. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  188. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  189. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  190. {splent_cli-1.48.2/src/splent_cli/services → splent_cli-1.50.0/src/splent_cli/commands/storage}/__init__.py +0 -0
  191. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/tokens_setup.py +0 -0
  192. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  193. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/commands/version.py +0 -0
  194. {splent_cli-1.48.2/src/splent_cli/utils → splent_cli-1.50.0/src/splent_cli/services}/__init__.py +0 -0
  195. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/compose.py +0 -0
  196. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/context.py +0 -0
  197. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/credentials.py +0 -0
  198. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/deploy_bundle.py +0 -0
  199. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/editable_refresh.py +0 -0
  200. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/frontend.py +0 -0
  201. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/index_refresh.py +0 -0
  202. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/marketplace.py +0 -0
  203. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/marketplace_api.py +0 -0
  204. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/marketplace_url.py +0 -0
  205. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/own_commands.py +0 -0
  206. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/preflight.py +0 -0
  207. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/pypi_quota.py +0 -0
  208. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/registry.py +0 -0
  209. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/release.py +0 -0
  210. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/release_gate.py +0 -0
  211. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/services/web_container.py +0 -0
  212. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_.gitignore.j2 +0 -0
  213. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
  214. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
  215. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
  216. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
  217. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
  218. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
  219. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
  220. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
  221. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
  222. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
  223. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
  224. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
  225. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
  226. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
  227. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
  228. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
  229. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
  230. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
  231. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
  232. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
  233. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
  234. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
  235. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
  236. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
  237. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
  238. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
  239. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
  240. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
  241. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
  242. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
  243. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
  244. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
  245. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
  246. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
  247. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/licenses/COPYING +0 -0
  248. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
  249. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
  250. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
  251. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
  252. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
  253. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
  254. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
  255. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
  256. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
  257. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
  258. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
  259. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
  260. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
  261. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +0 -0
  262. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
  263. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_app.css +0 -0
  264. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_app.js +0 -0
  265. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
  266. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +0 -0
  267. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
  268. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
  269. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_dropzone.css +0 -0
  270. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
  271. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
  272. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
  273. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_favicon.svg +0 -0
  274. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
  275. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
  276. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_own.css +0 -0
  277. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
  278. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
  279. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/Dockerfile.frontend +0 -0
  280. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/package-lock.json +0 -0
  281. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/package.json +0 -0
  282. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/build.js +0 -0
  283. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/legacy.js +0 -0
  284. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/lint.js +0 -0
  285. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/plugin.js +0 -0
  286. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/preset.js +0 -0
  287. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/toolchain/frontend/splent/singletons.json +0 -0
  288. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/archetype.py +0 -0
  289. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/cache_utils.py +0 -0
  290. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/ci_services.py +0 -0
  291. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/ci_workflow.py +0 -0
  292. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/command_loader.py +0 -0
  293. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  294. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/db_client.py +0 -0
  295. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/db_utils.py +0 -0
  296. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/decorators.py +0 -0
  297. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/deploy_freshness.py +0 -0
  298. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_installer.py +0 -0
  299. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_naming.py +0 -0
  300. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_params.py +0 -0
  301. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_requirements.py +0 -0
  302. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/feature_resolution.py +0 -0
  303. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/forge.py +0 -0
  304. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/frontend_licenses.py +0 -0
  305. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/git_url.py +0 -0
  306. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/integrity.py +0 -0
  307. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/io_utils.py +0 -0
  308. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/lifecycle.py +0 -0
  309. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/manifest.py +0 -0
  310. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/menu_keys.py +0 -0
  311. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/migration_lock.py +0 -0
  312. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/npm_semver.py +0 -0
  313. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/proc.py +0 -0
  314. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/redact.py +0 -0
  315. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/refinement_facts.py +0 -0
  316. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/running_version.py +0 -0
  317. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/template_drift.py +0 -0
  318. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/toml_edit.py +0 -0
  319. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/upgrade_plan.py +0 -0
  320. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
  321. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli/utils/uvl_params.py +0 -0
  322. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  323. {splent_cli-1.48.2 → splent_cli-1.50.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  324. {splent_cli-1.48.2 → splent_cli-1.50.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.48.2
3
+ Version: 1.50.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
  License-Expression: LGPL-3.0-or-later
@@ -16,7 +16,7 @@ Requires-Dist: packaging>=24.0
16
16
  Requires-Dist: python-dotenv>=1.0
17
17
  Requires-Dist: pyyaml>=6.0
18
18
  Requires-Dist: requests>=2.31.0
19
- Requires-Dist: splent_framework<2,>=1.22.0
19
+ Requires-Dist: splent_framework<2,>=1.30.0
20
20
  Requires-Dist: tomli_w>=1.0.0
21
21
  Provides-Extra: dev
22
22
  Requires-Dist: setuptools==80.3.1; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.48.2"
7
+ version = "1.50.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"
@@ -12,10 +12,6 @@ authors = [{ name = "DiversoLab", email = "diversolab@us.es" }]
12
12
  license = "LGPL-3.0-or-later"
13
13
  license-files = ["COPYING", "COPYING.LESSER"]
14
14
 
15
- # Everything the CLI imports at module level must be listed here (or come
16
- # with splent_framework, see tests/unit/test_declared_dependencies.py):
17
- # startup imports every command module, so one undeclared import breaks
18
- # `splent --help` on a clean `pip install splent_cli`.
19
15
  dependencies = [
20
16
  "click==8.1.8",
21
17
  "jinja2>=3.1",
@@ -23,15 +19,7 @@ dependencies = [
23
19
  "python-dotenv>=1.0",
24
20
  "pyyaml>=6.0",
25
21
  "requests>=2.31.0",
26
- # The CLI drives the framework (paths, feature order, migrations, the
27
- # app factory) and only works with the same major version of it. 1.22.0
28
- # brings splent_framework.params, the [tool.splent.config_schema] reader
29
- # product:validate, product:env and product:build share with the boot;
30
- # 1.18.0 resolves a feature to the copy feature:test names
31
- # (SPLENT_FEATURE_SOURCES) and brings the production defaults the deploy
32
- # templates explain; 1.17.0 the refinement rules product:validate shares
33
- # with the boot.
34
- "splent_framework>=1.22.0,<2",
22
+ "splent_framework>=1.30.0,<2",
35
23
  "tomli_w>=1.0.0",
36
24
  ]
37
25
 
@@ -76,10 +64,6 @@ package-dir = { "" = "src" }
76
64
  [tool.setuptools.packages.find]
77
65
  where = ["src"]
78
66
 
79
- # The templates product:create, feature:create and the rest render. They are
80
- # data, not modules, so without this a wheel from PyPI shipped none of them.
81
- # The build context of the frontend toolchain image too, so a CLI installed
82
- # from PyPI can build the image a feature with frontend/ builds in.
83
67
  [tool.setuptools.package-data]
84
68
  splent_cli = ["templates/**/*", "toolchain/**/*"]
85
69
 
@@ -87,9 +71,6 @@ splent_cli = ["templates/**/*", "toolchain/**/*"]
87
71
  testpaths = ["tests"]
88
72
  pythonpath = ["src"]
89
73
 
90
- # Canonical formatter/linter is Ruff (`splent lint` runs `ruff check` +
91
- # `ruff format`). Ruff's default line length (88) is the project standard;
92
- # set explicitly here so it is unambiguous.
93
74
  [tool.ruff]
94
75
  line-length = 88
95
76
 
@@ -45,6 +45,41 @@ def _quiet_unless(debug: bool):
45
45
  logging.disable(logging.NOTSET)
46
46
 
47
47
 
48
+ @contextlib.contextmanager
49
+ def _held_logs(debug: bool):
50
+ """Hold what is logged while the app is built here, for a command that
51
+ may run in the product's web container instead.
52
+
53
+ A build that fails on a package the CLI's image lacks hands the command
54
+ to the web container, which builds the app again and says there all
55
+ the build has to say. What the attempt here logged first (the features'
56
+ warnings, the traceback of the import that failed) came before it, as
57
+ if the command had failed. Yields the records held, which
58
+ :func:`_replay` hands to their loggers when the command does not move
59
+ after all. SPLENT_DEBUG shows them as they happen.
60
+ """
61
+ held: list[tuple[logging.Logger, logging.LogRecord]] = []
62
+ if debug:
63
+ yield held
64
+ return
65
+ handle = logging.Logger.handle
66
+
67
+ def hold(logger, record):
68
+ held.append((logger, record))
69
+
70
+ logging.Logger.handle = hold
71
+ try:
72
+ yield held
73
+ finally:
74
+ logging.Logger.handle = handle
75
+
76
+
77
+ def _replay(held) -> None:
78
+ """Log what :func:`_held_logs` held, as it would have been logged."""
79
+ for logger, record in held:
80
+ logger.handle(record)
81
+
82
+
48
83
  class SPLENTCLI(click.Group):
49
84
  """
50
85
  Main SPLENT CLI class.
@@ -211,14 +246,18 @@ class SPLENTCLI(click.Group):
211
246
  if context.is_override():
212
247
  self._delegate_to_the_product(ctx)
213
248
 
249
+ debug = bool(os.getenv("SPLENT_DEBUG"))
214
250
  try:
215
- app = get_app()
251
+ with _held_logs(debug) as held:
252
+ app = get_app()
216
253
  except Exception as exc:
217
254
  missing = _missing_module(exc)
218
255
  if missing is None:
256
+ _replay(held)
219
257
  raise
220
258
  # Only then: where the app builds here, the command runs here
221
259
  # with the tools the CLI image has (ruff formats migrations).
260
+ # The web container builds it again and logs what it logs.
222
261
  self._delegate_to_the_product(ctx)
223
262
  product = context.active_app() or "The product"
224
263
  raise click.ClickException(
@@ -227,6 +266,7 @@ class SPLENTCLI(click.Group):
227
266
  " Start the product (splent product:up --dev) and run the "
228
267
  "command again: it then runs in the product's web container."
229
268
  ) from exc
269
+ _replay(held)
230
270
  if getattr(command, "requires_db", False):
231
271
  if not check_db_connection(app):
232
272
  raise SystemExit(1)
@@ -39,8 +39,8 @@ def _validate_feature(ctx, param, value):
39
39
  or (at and not _plain(version, _VERSION_RE))
40
40
  ):
41
41
  raise click.BadParameter(
42
- "must be a feature name such as splent_feature_auth, optionally"
43
- " with a namespace and a version (splent-io/splent_feature_auth@v1.2.0)."
42
+ "must be a feature name such as splent_feature_notes, optionally"
43
+ " with a namespace and a version (splent-io/splent_feature_notes@v1.2.0)."
44
44
  )
45
45
  ns, name, version = parse_feature_entry(value)
46
46
  return {
@@ -366,7 +366,7 @@ def check_infra():
366
366
 
367
367
  data = load_toml(pyproject_path, what="pyproject.toml")
368
368
 
369
- env = os.getenv("SPLENT_ENV", "dev")
369
+ env = context.resolve_env()
370
370
  features = read_features_from_data(data, env)
371
371
 
372
372
  # Every compose file is read through the product's merged .env, the same one
@@ -32,6 +32,13 @@ _SYSTEM_DIRS = {
32
32
  "/tmp",
33
33
  "/usr",
34
34
  "/var",
35
+ # macOS: its own top level, and /private, where /etc, /tmp and /var lead.
36
+ "/Applications",
37
+ "/Library",
38
+ "/System",
39
+ "/Users",
40
+ "/Volumes",
41
+ "/private",
35
42
  }
36
43
 
37
44
  # What a directory of code has at its top and a directory of uploads never
@@ -77,6 +84,12 @@ def _configured_dir(app) -> str:
77
84
  return os.getenv("PROTECTED_UPLOADS_DIR", "").strip()
78
85
 
79
86
 
87
+ def _system_dirs() -> set:
88
+ """The system directories, and where each leads: on macOS /etc is a link
89
+ to /private/etc, which the realpath of a configured /etc gives."""
90
+ return _SYSTEM_DIRS | {os.path.realpath(path) for path in _SYSTEM_DIRS}
91
+
92
+
80
93
  def _refuse(path: str, real: str, why: str):
81
94
  shown = path if path == real else f"{path} (resolves to {real})"
82
95
  raise UploadsDirError(f"{shown} {why}. Nothing was deleted.")
@@ -124,7 +137,7 @@ def resolve_uploads_dir(app=None) -> str:
124
137
  )
125
138
 
126
139
  real = os.path.realpath(uploads_dir)
127
- if real == os.sep or real in _SYSTEM_DIRS:
140
+ if real == os.sep or real in _system_dirs():
128
141
  _refuse(uploads_dir, real, "is a system directory")
129
142
  if real == os.path.realpath(os.path.expanduser("~")):
130
143
  _refuse(uploads_dir, real, "is the home directory")
@@ -289,8 +302,8 @@ def clear_uploads(env_dev, env_prod, yes):
289
302
 
290
303
  if not yes:
291
304
  click.confirm(
292
- f"Delete every file in {uploads_dir}? The datasets and documents "
293
- "the app serves from it cannot be recovered.",
305
+ f"Delete every file in {uploads_dir}? What the features stored "
306
+ "there, and the app serves from it, cannot be recovered.",
294
307
  abort=True,
295
308
  )
296
309
 
@@ -735,7 +735,7 @@ def run_all_product_check(
735
735
  if not labeled_paths:
736
736
  return []
737
737
  if env is None:
738
- env = os.getenv("SPLENT_ENV") or "dev"
738
+ env = context.resolve_env()
739
739
  labeled_contracts = [(lbl, _read_contract(path)) for lbl, path in labeled_paths]
740
740
  findings = _analyse_all(labeled_contracts) + version_floor_findings(
741
741
  labeled_paths
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ import hashlib
2
3
  import logging
3
4
  import os
4
5
  import posixpath
@@ -553,6 +554,31 @@ def _find_webpack(workspace, product, org_safe, base_name, version):
553
554
  return None
554
555
 
555
556
 
557
+ def _one_watcher(webpack_file: str, product_root: str, shell_cmd: str) -> str:
558
+ """The shell line that watches ``webpack_file`` and stops its watcher
559
+ before, where webpack runs.
560
+
561
+ Each start of a product in development (product:run --dev) compiles
562
+ with --watch again, and a watcher used to outlive the one that started
563
+ it: eight starts left eight generations of npm, sh and webpack, 2.4 GB
564
+ of a 7.8 GB Docker VM. The watcher now runs in a session of its own
565
+ (setsid), whose id it leaves in a file named after its bundle, and the
566
+ next start ends that whole group first, if it is still a webpack one.
567
+ """
568
+ folder = "/tmp/splent-watch-$(id -u)"
569
+ name = hashlib.sha1(webpack_file.encode()).hexdigest()[:16]
570
+ pidfile = f"{folder}/{name}.pid"
571
+ inner = f'echo $$ > "{pidfile}"; exec {shell_cmd}'
572
+ return (
573
+ f'mkdir -p "{folder}"; '
574
+ f'if [ -f "{pidfile}" ]; then '
575
+ f'old="$(cat "{pidfile}")"; '
576
+ f'if grep -qa webpack "/proc/$old/cmdline" 2>/dev/null; then '
577
+ f'kill -TERM -- "-$old" 2>/dev/null || true; fi; fi; '
578
+ f"cd {shlex.quote(product_root)} && exec setsid bash -c {shlex.quote(inner)}"
579
+ )
580
+
581
+
556
582
  def _compile_in_container(
557
583
  container_id,
558
584
  feature,
@@ -645,6 +671,8 @@ def _compile_in_container(
645
671
 
646
672
  shell_cmd = " ".join(shlex.quote(p) for p in cmd_parts)
647
673
  cd_cmd = f"cd {shlex.quote(product_root)} && {shell_cmd}"
674
+ if watch and not production:
675
+ cd_cmd = _one_watcher(webpack_file, product_root, shell_cmd)
648
676
 
649
677
  # Inside a container: run webpack directly.
650
678
  # From the host: run via `docker exec` so webpack uses the container's
@@ -21,10 +21,12 @@ from pathlib import Path
21
21
 
22
22
  from splent_cli.services import context
23
23
  from splent_cli.commands.feature.feature_release import (
24
+ REVISION_ID_MAX,
24
25
  _interactive,
25
26
  infer_contract,
26
27
  merge_preserved,
27
28
  requirement_problems,
29
+ revision_id_problems,
28
30
  write_contract,
29
31
  )
30
32
  from splent_cli.utils.feature_resolution import package_identifier, resolve_feature_ref
@@ -444,7 +446,25 @@ def feature_contract(feature_ref, write, inject_config):
444
446
  for problem in problems:
445
447
  click.secho(f" {problem}", fg="red")
446
448
  click.echo(
447
- ' Write a short name with an optional PEP 440 specifier, such as "auth>=1.20.0".'
449
+ ' Write a short name with an optional PEP 440 specifier, such as "notes>=1.20.0".'
450
+ )
451
+ raise SystemExit(1)
452
+
453
+ # A migration no database can record: refused here, in the dry run too,
454
+ # as feature:release refuses it, before anyone releases it.
455
+ long_ids = revision_id_problems(str(cache_path), name)
456
+ if long_ids:
457
+ click.secho(
458
+ f" ❌ These revision ids are longer than the {REVISION_ID_MAX} "
459
+ "characters alembic_version.version_num holds:",
460
+ fg="red",
461
+ )
462
+ for problem in long_ids:
463
+ click.secho(f" {problem}", fg="red")
464
+ click.echo(
465
+ " The upgrade would run the migration and then fail to record "
466
+ "it.\n Shorten the revision id, and every down_revision that "
467
+ "names it."
448
468
  )
449
469
  raise SystemExit(1)
450
470
 
@@ -230,7 +230,7 @@ def feature_install(feature_identifier, env_scope, mode, version):
230
230
  """
231
231
  product = context.require_app()
232
232
  workspace = str(context.workspace())
233
- env_name = os.getenv("SPLENT_ENV", "dev")
233
+ env_name = context.resolve_env()
234
234
 
235
235
  # ── Parse identifier ──────────────────────────────────────────────
236
236
  namespace, namespace_github, namespace_fs, feature_name = (