splent-cli 1.8.0__tar.gz → 1.9.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 (167) hide show
  1. {splent_cli-1.8.0/src/splent_cli.egg-info → splent_cli-1.9.1}/PKG-INFO +1 -1
  2. {splent_cli-1.8.0 → splent_cli-1.9.1}/pyproject.toml +1 -1
  3. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_migrate.py +39 -3
  4. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_release.py +18 -1
  5. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_xray.py +10 -7
  6. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_deploy.py +6 -2
  7. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_env.py +6 -1
  8. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_add_feature.py +25 -0
  9. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/services/release.py +25 -3
  10. {splent_cli-1.8.0 → splent_cli-1.9.1/src/splent_cli.egg-info}/PKG-INFO +1 -1
  11. {splent_cli-1.8.0 → splent_cli-1.9.1}/LICENSE +0 -0
  12. {splent_cli-1.8.0 → splent_cli-1.9.1}/README.md +0 -0
  13. {splent_cli-1.8.0 → splent_cli-1.9.1}/setup.cfg +0 -0
  14. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/__init__.py +0 -0
  15. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/__main__.py +0 -0
  16. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/cli.py +0 -0
  17. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/__init__.py +0 -0
  18. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/__init__.py +0 -0
  19. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  20. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  21. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  22. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  23. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_size.py +0 -0
  24. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_status.py +0 -0
  25. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  26. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  27. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/__init__.py +0 -0
  28. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_deps.py +0 -0
  29. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_docker.py +0 -0
  30. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_env.py +0 -0
  31. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_features.py +0 -0
  32. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_github.py +0 -0
  33. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_infra.py +0 -0
  34. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_product.py +0 -0
  35. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_pypi.py +0 -0
  36. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  37. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/clear/__init__.py +0 -0
  38. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/clear/clear_build.py +0 -0
  39. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/clear/clear_log.py +0 -0
  40. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  41. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/command/__init__.py +0 -0
  42. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/command/command_create.py +0 -0
  43. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/coverage.py +0 -0
  44. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_console.py +0 -0
  45. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_dump.py +0 -0
  46. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_reset.py +0 -0
  47. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_restore.py +0 -0
  48. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_rollback.py +0 -0
  49. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_seed.py +0 -0
  50. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_status.py +0 -0
  51. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  52. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/doctor.py +0 -0
  53. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/env/env_list.py +0 -0
  54. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/env/env_set.py +0 -0
  55. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/env/env_show.py +0 -0
  56. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/export/__init__.py +0 -0
  57. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/export/export_puml.py +0 -0
  58. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_add.py +0 -0
  59. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  60. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  61. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  62. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  63. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  64. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_contract.py +0 -0
  65. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_create.py +0 -0
  66. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  67. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  68. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  69. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  70. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_env.py +0 -0
  71. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  72. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_git.py +0 -0
  73. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  74. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  75. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  76. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  77. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  78. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_install.py +0 -0
  79. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_list.py +0 -0
  80. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_order.py +0 -0
  81. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  82. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  83. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  84. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  85. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  86. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  87. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  88. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_search.py +0 -0
  89. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_status.py +0 -0
  90. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  91. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_test.py +0 -0
  92. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  93. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  94. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  95. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  96. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/lint.py +0 -0
  97. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/locust.py +0 -0
  98. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/__init__.py +0 -0
  99. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  100. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_build.py +0 -0
  101. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_clean.py +0 -0
  102. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_commands.py +0 -0
  103. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_config.py +0 -0
  104. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_configure.py +0 -0
  105. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_console.py +0 -0
  106. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_containers.py +0 -0
  107. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_create.py +0 -0
  108. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_derive.py +0 -0
  109. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_deselect.py +0 -0
  110. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_down.py +0 -0
  111. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_drift.py +0 -0
  112. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_list.py +0 -0
  113. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_logs.py +0 -0
  114. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_missing.py +0 -0
  115. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_port.py +0 -0
  116. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_release.py +0 -0
  117. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_resolve.py +0 -0
  118. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_restart.py +0 -0
  119. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_routes.py +0 -0
  120. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_run.py +0 -0
  121. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_select.py +0 -0
  122. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_shell.py +0 -0
  123. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_signals.py +0 -0
  124. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  125. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_test.py +0 -0
  126. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_up.py +0 -0
  127. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/product/product_validate.py +0 -0
  128. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/release/__init__.py +0 -0
  129. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/release/release_core.py +0 -0
  130. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/selenium.py +0 -0
  131. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/__init__.py +0 -0
  132. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  133. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  134. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_create.py +0 -0
  135. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  136. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_features.py +0 -0
  137. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  138. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_info.py +0 -0
  139. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_list.py +0 -0
  140. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  141. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/tokens_setup.py +0 -0
  142. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  143. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/commands/version.py +0 -0
  144. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/services/__init__.py +0 -0
  145. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/services/compose.py +0 -0
  146. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/services/context.py +0 -0
  147. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/services/preflight.py +0 -0
  148. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/__init__.py +0 -0
  149. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/cache_utils.py +0 -0
  150. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/command_loader.py +0 -0
  151. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/contract_freshness.py +0 -0
  152. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/db_utils.py +0 -0
  153. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/decorators.py +0 -0
  154. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/dynamic_imports.py +0 -0
  155. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/feature_installer.py +0 -0
  156. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/feature_utils.py +0 -0
  157. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/git_url.py +0 -0
  158. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/integrity.py +0 -0
  159. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/lifecycle.py +0 -0
  160. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/manifest.py +0 -0
  161. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/path_utils.py +0 -0
  162. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli/utils/template_drift.py +0 -0
  163. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli.egg-info/SOURCES.txt +0 -0
  164. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  165. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli.egg-info/entry_points.txt +0 -0
  166. {splent_cli-1.8.0 → splent_cli-1.9.1}/src/splent_cli.egg-info/requires.txt +0 -0
  167. {splent_cli-1.8.0 → splent_cli-1.9.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.8.0
3
+ Version: 1.9.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.8.0"
7
+ version = "1.9.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"
@@ -2,6 +2,7 @@ import os
2
2
 
3
3
  import click
4
4
  from flask_migrate import migrate as alembic_migrate
5
+ from flask_migrate import revision as alembic_revision
5
6
 
6
7
  from splent_cli.utils.decorators import requires_db
7
8
  from splent_cli.services import context
@@ -54,14 +55,31 @@ def _is_empty_migration(path: str) -> bool:
54
55
  @click.option(
55
56
  "-m", "message", default=None, help="Migration message (defaults to feature name)."
56
57
  )
58
+ @click.option(
59
+ "--empty",
60
+ is_flag=True,
61
+ help=(
62
+ "Generate a blank migration (no autogenerate) and keep it for manual "
63
+ "editing. Required for refinement features that add columns to a base "
64
+ "feature's table — Alembic cannot autodetect runtime-injected mixins."
65
+ ),
66
+ )
57
67
  @context.requires_product
58
- def db_migrate(feature, message):
68
+ def db_migrate(feature, message, empty):
59
69
  """
60
70
  Generate new migration files for features that have schema changes,
61
71
  then apply all pending migrations.
62
72
 
63
- Empty migrations (no schema changes detected) are automatically removed.
73
+ Empty migrations (no schema changes detected) are automatically removed,
74
+ unless ``--empty`` is passed, in which case a blank migration is created
75
+ and kept so you can write ``upgrade()``/``downgrade()`` by hand.
64
76
  """
77
+ if empty and not feature:
78
+ click.echo(
79
+ click.style("❌ --empty requires a FEATURE argument.", fg="red")
80
+ )
81
+ raise SystemExit(1)
82
+
65
83
  if feature:
66
84
  dirs = {}
67
85
  mdir = MigrationManager.get_feature_migration_dir(feature)
@@ -108,7 +126,10 @@ def db_migrate(feature, message):
108
126
  alembic_logger.setLevel(logging.WARNING)
109
127
 
110
128
  try:
111
- alembic_migrate(directory=mdir, message=message or feat)
129
+ if empty:
130
+ alembic_revision(directory=mdir, message=message or feat)
131
+ else:
132
+ alembic_migrate(directory=mdir, message=message or feat)
112
133
  except Exception as e:
113
134
  if os.getenv("SPLENT_DEBUG"):
114
135
  click.secho(
@@ -119,6 +140,21 @@ def db_migrate(feature, message):
119
140
 
120
141
  after = _count_versions(mdir)
121
142
 
143
+ # A blank migration requested with --empty is intentional: keep it so the
144
+ # user can fill in upgrade()/downgrade() by hand (e.g. refinement columns).
145
+ if empty:
146
+ if after > before:
147
+ click.echo(
148
+ click.style(
149
+ f" 📝 {feat}: blank migration generated — "
150
+ f"edit upgrade()/downgrade() and run 'splent db:upgrade'",
151
+ fg="cyan",
152
+ )
153
+ )
154
+ else:
155
+ click.echo(click.style(f" ✔ {feat}: up to date", fg="green"))
156
+ continue
157
+
122
158
  # If a new file was generated, check if it's empty (no real schema changes)
123
159
  if after > before:
124
160
  versions_dir = os.path.join(mdir, "versions")
@@ -237,10 +237,27 @@ def _extract_translations(translations_dir: Path) -> list[str]:
237
237
  return locales
238
238
 
239
239
 
240
+ def _strip_docstrings_and_comments(text: str) -> str:
241
+ """Remove triple-quoted strings (docstrings) and ``#`` comments.
242
+
243
+ Signal inference scans for ``define_signal("...")`` / ``connect_signal("...")``
244
+ calls. The scaffolded ``signals.py`` ships *examples* of those calls inside a
245
+ module docstring; without stripping them the inferred contract would list
246
+ example signal names the feature never actually emits (e.g. ``item-created``),
247
+ causing spurious collisions in ``feature:xray --validate``.
248
+ """
249
+ # Drop triple-quoted blocks (both ''' and """).
250
+ text = re.sub(r'""".*?"""', "", text, flags=re.DOTALL)
251
+ text = re.sub(r"'''.*?'''", "", text, flags=re.DOTALL)
252
+ # Drop full-line and trailing comments.
253
+ text = re.sub(r"#.*", "", text)
254
+ return text
255
+
256
+
240
257
  def _extract_signals(signals_path: Path) -> tuple[list[str], list[str]]:
241
258
  if not signals_path.exists():
242
259
  return [], []
243
- text = signals_path.read_text()
260
+ text = _strip_docstrings_and_comments(signals_path.read_text())
244
261
  provided = sorted(
245
262
  set(re.findall(r"""define_signal\s*\(\s*['"]([^'"]+)['"]""", text))
246
263
  )
@@ -478,16 +478,19 @@ def feature_xray(feature_ref, filter_cat, validate, full):
478
478
  if not bp_collisions:
479
479
  ok += 1
480
480
 
481
- # 5. Commands: check for name collisions
482
- all_commands: dict[str, list[str]] = {}
481
+ # 5. Commands: feature-contributed commands are namespaced under
482
+ # ``feature:<name> <cmd>``, so the same command name in two different
483
+ # features (e.g. the scaffold's example ``hello``) never actually collides.
484
+ # We only flag a real duplicate *within* a single feature.
485
+ cmd_collisions = {}
483
486
  for name, fdata in feature_data.items():
484
- for cmd in fdata["contract"].get("provides", {}).get("commands", []):
485
- all_commands.setdefault(cmd, []).append(name)
486
-
487
- cmd_collisions = {c: fs for c, fs in all_commands.items() if len(fs) > 1}
487
+ cmds = fdata["contract"].get("provides", {}).get("commands", [])
488
+ dupes = {c for c in cmds if cmds.count(c) > 1}
489
+ for c in dupes:
490
+ cmd_collisions.setdefault(f"{name}:{c}", []).append(name)
488
491
  for cmd, features_list in cmd_collisions.items():
489
492
  click.secho(
490
- f" ❌ Command '{cmd}' — collision: {', '.join(features_list)}",
493
+ f" ❌ Command '{cmd}' — duplicated within feature",
491
494
  fg="red",
492
495
  )
493
496
  fail += 1
@@ -268,13 +268,17 @@ def product_deploy(down, ci):
268
268
  import time
269
269
 
270
270
  healthy = False
271
- for attempt in range(15):
271
+ # The production web container is named ``<product>_web_deploy`` —
272
+ # NOT ``<product>_web`` (that is the dev container). A cold start runs
273
+ # migrations and compiles assets before gunicorn binds, so allow a
274
+ # generous window (~90s) before declaring the app unhealthy.
275
+ for attempt in range(45):
272
276
  try:
273
277
  result = subprocess.run(
274
278
  [
275
279
  "docker",
276
280
  "exec",
277
- f"{product}_web",
281
+ f"{product}_web_deploy",
278
282
  "bash",
279
283
  "-c",
280
284
  "curl -s -o /dev/null -w '%{http_code}' http://localhost:5000/",
@@ -172,7 +172,12 @@ def product_env(generate, merge, env_name, process_all):
172
172
  )
173
173
  raise SystemExit(1)
174
174
 
175
- target_env = os.path.join(docker_dir, ".env")
175
+ # Each environment writes to its own file so a `--prod` merge never
176
+ # clobbers the development `.env` (which the dev containers read via
177
+ # env_file). dev → .env, prod → .env.deploy.
178
+ target_env = os.path.join(
179
+ docker_dir, ".env.deploy" if env_name == "prod" else ".env"
180
+ )
176
181
  if base_env != target_env:
177
182
  shutil.copyfile(base_env, target_env)
178
183
 
@@ -74,6 +74,31 @@ def _scan_feature_deps(
74
74
  ) -> set[str]:
75
75
  """Scan a feature's source for dependencies. Returns set of short_names."""
76
76
  deps = set()
77
+
78
+ # Refinement features declare the feature they refine in pyproject.toml
79
+ # ([tool.splent.refinement].refines). That is a hard dependency — the base
80
+ # feature must load first — so surface it as a constraint even when there is
81
+ # no Python import or FK between them.
82
+ pyproject_path = os.path.join(feature_path, "pyproject.toml")
83
+ if os.path.isfile(pyproject_path):
84
+ try:
85
+ import tomllib
86
+
87
+ with open(pyproject_path, "rb") as fh:
88
+ pydata = tomllib.load(fh)
89
+ refines = (
90
+ pydata.get("tool", {})
91
+ .get("splent", {})
92
+ .get("refinement", {})
93
+ .get("refines")
94
+ )
95
+ if refines and refines != feature_pkg and refines in all_packages:
96
+ short = pkg_to_short.get(refines)
97
+ if short:
98
+ deps.add(short)
99
+ except Exception:
100
+ pass
101
+
77
102
  src_dir = os.path.join(feature_path, "src", "splent_io", feature_pkg)
78
103
  if not os.path.isdir(src_dir):
79
104
  return deps
@@ -260,17 +260,39 @@ def build_and_upload_pypi(path: str):
260
260
  raise SystemExit(1)
261
261
 
262
262
  click.echo(" pypi uploading...")
263
+
264
+ # Expand dist/* ourselves: subprocess runs without a shell, so a literal
265
+ # "dist/*" would be passed verbatim to twine and never match any file.
266
+ import glob
267
+
268
+ dist_files = sorted(glob.glob(os.path.join(path, "dist", "*")))
269
+ if not dist_files:
270
+ click.secho(" error: no built artifacts found in dist/", fg="red")
271
+ raise SystemExit(1)
272
+
273
+ # twine reads TWINE_USERNAME / TWINE_PASSWORD from the environment. The CLI
274
+ # loads them from the workspace .env (load_dotenv), so they are already in
275
+ # os.environ — pass it through explicitly. --non-interactive makes twine fail
276
+ # loudly instead of blocking on a credentials prompt; --skip-existing keeps
277
+ # re-runs idempotent (a half-uploaded release can be retried safely).
263
278
  try:
264
279
  subprocess.run(
265
- [sys.executable, "-m", "twine", "upload", "dist/*"],
280
+ [
281
+ sys.executable,
282
+ "-m",
283
+ "twine",
284
+ "upload",
285
+ "--non-interactive",
286
+ "--skip-existing",
287
+ *dist_files,
288
+ ],
266
289
  env=os.environ.copy(),
267
290
  check=True,
268
291
  cwd=path,
269
- capture_output=True,
270
292
  )
271
293
  except subprocess.CalledProcessError as e:
272
294
  click.secho(
273
- f" error: PyPI upload failed: {e.stderr.strip() if e.stderr else ''}",
295
+ f" error: PyPI upload failed (exit {e.returncode}).",
274
296
  fg="red",
275
297
  )
276
298
  click.secho(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.8.0
3
+ Version: 1.9.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