splent-cli 1.12.0__tar.gz → 1.16.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 (207) hide show
  1. {splent_cli-1.12.0/src/splent_cli.egg-info → splent_cli-1.16.0}/PKG-INFO +2 -1
  2. {splent_cli-1.12.0 → splent_cli-1.16.0}/pyproject.toml +2 -1
  3. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/cli.py +14 -2
  4. splent_cli-1.16.0/src/splent_cli/commands/check/check_contracts.py +212 -0
  5. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_deps.py +13 -8
  6. splent_cli-1.16.0/src/splent_cli/commands/check/check_pypi.py +127 -0
  7. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_pyproject.py +15 -5
  8. splent_cli-1.16.0/src/splent_cli/commands/check/check_releases.py +394 -0
  9. splent_cli-1.16.0/src/splent_cli/commands/database/db_dump.py +188 -0
  10. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_reset.py +24 -10
  11. splent_cli-1.16.0/src/splent_cli/commands/database/db_restore.py +171 -0
  12. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_rollback.py +4 -9
  13. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_seed.py +5 -8
  14. splent_cli-1.16.0/src/splent_cli/commands/database/db_upgrade.py +229 -0
  15. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/export/export_puml.py +3 -5
  16. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_add.py +38 -15
  17. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_attach.py +60 -24
  18. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_clean.py +5 -35
  19. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_clone.py +2 -35
  20. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_contract.py +53 -1
  21. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_detach.py +13 -8
  22. splent_cli-1.16.0/src/splent_cli/commands/feature/feature_info.py +213 -0
  23. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_install.py +5 -24
  24. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_order.py +7 -9
  25. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_outdated.py +5 -28
  26. splent_cli-1.16.0/src/splent_cli/commands/feature/feature_pip_install.py +265 -0
  27. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_release.py +308 -9
  28. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_remove.py +61 -29
  29. splent_cli-1.16.0/src/splent_cli/commands/feature/feature_search.py +239 -0
  30. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_upgrade.py +9 -32
  31. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_versions.py +13 -83
  32. splent_cli-1.16.0/src/splent_cli/commands/locust.py +140 -0
  33. splent_cli-1.16.0/src/splent_cli/commands/marketplace/login.py +164 -0
  34. splent_cli-1.16.0/src/splent_cli/commands/marketplace/logout.py +101 -0
  35. splent_cli-1.16.0/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +298 -0
  36. splent_cli-1.16.0/src/splent_cli/commands/marketplace/marketplace_index.py +194 -0
  37. splent_cli-1.16.0/src/splent_cli/commands/marketplace/whoami.py +140 -0
  38. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_build.py +90 -14
  39. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_configure.py +13 -6
  40. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_create.py +61 -57
  41. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_deploy.py +119 -18
  42. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_port.py +35 -6
  43. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_release.py +66 -7
  44. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_up.py +10 -2
  45. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_validate.py +40 -8
  46. splent_cli-1.16.0/src/splent_cli/commands/release/release_core.py +129 -0
  47. splent_cli-1.16.0/src/splent_cli/commands/release/release_resume.py +399 -0
  48. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_create.py +52 -0
  49. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_fetch.py +41 -0
  50. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_info.py +93 -0
  51. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_list.py +68 -0
  52. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_migrate_catalog.py +180 -0
  53. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_outdated.py +185 -0
  54. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_pin.py +89 -0
  55. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_publish.py +402 -0
  56. splent_cli-1.16.0/src/splent_cli/commands/spl/spl_utils.py +91 -0
  57. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/uvl/uvl_utils.py +22 -8
  58. splent_cli-1.16.0/src/splent_cli/services/credentials.py +355 -0
  59. splent_cli-1.16.0/src/splent_cli/services/index_refresh.py +95 -0
  60. splent_cli-1.16.0/src/splent_cli/services/marketplace.py +560 -0
  61. splent_cli-1.16.0/src/splent_cli/services/marketplace_api.py +635 -0
  62. splent_cli-1.16.0/src/splent_cli/services/marketplace_url.py +149 -0
  63. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/services/preflight.py +24 -6
  64. splent_cli-1.16.0/src/splent_cli/services/registry.py +666 -0
  65. splent_cli-1.16.0/src/splent_cli/services/release.py +1292 -0
  66. splent_cli-1.16.0/src/splent_cli/services/release_gate.py +1025 -0
  67. splent_cli-1.16.0/src/splent_cli/services/spl_store.py +760 -0
  68. splent_cli-1.16.0/src/splent_cli/utils/archetype.py +56 -0
  69. splent_cli-1.16.0/src/splent_cli/utils/command_loader.py +112 -0
  70. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/dynamic_imports.py +10 -0
  71. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/feature_utils.py +126 -0
  72. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/git_url.py +52 -8
  73. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/template_drift.py +6 -0
  74. {splent_cli-1.12.0 → splent_cli-1.16.0/src/splent_cli.egg-info}/PKG-INFO +2 -1
  75. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli.egg-info/SOURCES.txt +22 -0
  76. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli.egg-info/requires.txt +1 -0
  77. splent_cli-1.12.0/src/splent_cli/commands/check/check_pypi.py +0 -118
  78. splent_cli-1.12.0/src/splent_cli/commands/database/db_dump.py +0 -86
  79. splent_cli-1.12.0/src/splent_cli/commands/database/db_restore.py +0 -117
  80. splent_cli-1.12.0/src/splent_cli/commands/database/db_upgrade.py +0 -104
  81. splent_cli-1.12.0/src/splent_cli/commands/feature/feature_pip_install.py +0 -100
  82. splent_cli-1.12.0/src/splent_cli/commands/feature/feature_search.py +0 -144
  83. splent_cli-1.12.0/src/splent_cli/commands/locust.py +0 -271
  84. splent_cli-1.12.0/src/splent_cli/commands/release/release_core.py +0 -68
  85. splent_cli-1.12.0/src/splent_cli/commands/spl/spl_create.py +0 -48
  86. splent_cli-1.12.0/src/splent_cli/commands/spl/spl_fetch.py +0 -36
  87. splent_cli-1.12.0/src/splent_cli/commands/spl/spl_info.py +0 -66
  88. splent_cli-1.12.0/src/splent_cli/commands/spl/spl_list.py +0 -59
  89. splent_cli-1.12.0/src/splent_cli/commands/spl/spl_utils.py +0 -89
  90. splent_cli-1.12.0/src/splent_cli/services/release.py +0 -614
  91. splent_cli-1.12.0/src/splent_cli/utils/command_loader.py +0 -42
  92. {splent_cli-1.12.0 → splent_cli-1.16.0}/LICENSE +0 -0
  93. {splent_cli-1.12.0 → splent_cli-1.16.0}/README.md +0 -0
  94. {splent_cli-1.12.0 → splent_cli-1.16.0}/setup.cfg +0 -0
  95. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/__init__.py +0 -0
  96. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/__main__.py +0 -0
  97. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/__init__.py +0 -0
  98. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  99. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  100. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  101. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  102. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  103. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  104. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  105. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  106. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  107. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/__init__.py +0 -0
  108. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  109. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_env.py +0 -0
  110. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_features.py +0 -0
  111. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_github.py +0 -0
  112. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_infra.py +0 -0
  113. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/check/check_product.py +0 -0
  114. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  115. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  116. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  117. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  118. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/command/__init__.py +0 -0
  119. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/command/command_create.py +0 -0
  120. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/coverage.py +0 -0
  121. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_console.py +0 -0
  122. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
  123. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/database/db_status.py +0 -0
  124. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/doctor.py +0 -0
  125. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/env/env_list.py +0 -0
  126. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/env/env_set.py +0 -0
  127. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/env/env_show.py +0 -0
  128. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/export/__init__.py +0 -0
  129. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  130. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  131. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  132. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  133. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  134. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  135. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  136. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
  137. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  138. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  139. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  140. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  141. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
  142. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  143. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  144. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  145. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  146. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
  147. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  148. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  149. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  150. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
  151. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  152. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
  153. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  154. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/lint.py +0 -0
  155. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/__init__.py +0 -0
  156. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  157. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  158. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_commands.py +0 -0
  159. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_config.py +0 -0
  160. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_console.py +0 -0
  161. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_containers.py +0 -0
  162. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_derive.py +0 -0
  163. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  164. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_down.py +0 -0
  165. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  166. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_env.py +0 -0
  167. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_list.py +0 -0
  168. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  169. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  170. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  171. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  172. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  173. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_run.py +0 -0
  174. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_select.py +0 -0
  175. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_shell.py +0 -0
  176. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  177. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  178. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/product/product_test.py +0 -0
  179. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/release/__init__.py +0 -0
  180. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/selenium.py +0 -0
  181. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  182. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  183. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
  184. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  185. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  186. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  187. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  188. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/tokens_setup.py +0 -0
  189. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/commands/version.py +0 -0
  190. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/services/__init__.py +0 -0
  191. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/services/compose.py +0 -0
  192. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/services/context.py +0 -0
  193. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/__init__.py +0 -0
  194. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/cache_utils.py +0 -0
  195. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  196. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/db_utils.py +0 -0
  197. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/decorators.py +0 -0
  198. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/feature_installer.py +0 -0
  199. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/integrity.py +0 -0
  200. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/io_utils.py +0 -0
  201. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/lifecycle.py +0 -0
  202. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/manifest.py +0 -0
  203. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/path_utils.py +0 -0
  204. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli/utils/proc.py +0 -0
  205. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  206. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  207. {splent_cli-1.12.0 → splent_cli-1.16.0}/src/splent_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splent_cli
3
- Version: 1.12.0
3
+ Version: 1.16.0
4
4
  Summary: SPLENT-CLI is a CLI to be able to work on your development more easily.
5
5
  Author-email: DiversoLab <diversolab@us.es>
6
6
  Project-URL: Homepage, https://github.com/diverso-lab/splent_cli
@@ -10,6 +10,7 @@ Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: click==8.1.8
12
12
  Requires-Dist: packaging>=24.0
13
+ Requires-Dist: pyyaml>=6.0
13
14
  Requires-Dist: requests>=2.31.0
14
15
  Requires-Dist: tomli_w>=1.0.0
15
16
  Provides-Extra: dev
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.12.0"
7
+ version = "1.16.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"
@@ -14,6 +14,7 @@ license-files = ["LICENSE"]
14
14
  dependencies = [
15
15
  "click==8.1.8",
16
16
  "packaging>=24.0",
17
+ "pyyaml>=6.0",
17
18
  "requests>=2.31.0",
18
19
  "tomli_w>=1.0.0",
19
20
  ]
@@ -3,9 +3,7 @@ import os
3
3
  import click
4
4
  from dotenv import load_dotenv
5
5
 
6
- from splent_cli.utils.dynamic_imports import get_app
7
6
  from splent_cli.utils.command_loader import load_commands
8
- from splent_cli.utils.db_utils import check_db_connection
9
7
 
10
8
  load_dotenv()
11
9
 
@@ -40,6 +38,8 @@ class SPLENTCLI(click.Group):
40
38
 
41
39
  self._feature_cmds_cache: dict[str, click.BaseCommand] = {}
42
40
  try:
41
+ from splent_cli.utils.dynamic_imports import get_app
42
+
43
43
  app = get_app()
44
44
  with app.app_context():
45
45
  registry = app.extensions.get("splent_feature_commands", {})
@@ -84,6 +84,9 @@ class SPLENTCLI(click.Group):
84
84
  command = self.get_command(ctx, cmd_name)
85
85
 
86
86
  if command and getattr(command, "requires_app", False):
87
+ from splent_cli.utils.dynamic_imports import get_app
88
+ from splent_cli.utils.db_utils import check_db_connection
89
+
87
90
  app = get_app()
88
91
  if getattr(command, "requires_db", False):
89
92
  if not check_db_connection(app):
@@ -109,6 +112,15 @@ class SPLENTCLI(click.Group):
109
112
  "🧬 SPL & Variability": [
110
113
  cmd for cmd in all_cmds if cmd.startswith(("spl:", "uvl:"))
111
114
  ],
115
+ "🛒 Marketplace": [
116
+ cmd
117
+ for cmd in all_cmds
118
+ # login/logout/whoami are bare on purpose (docker-login
119
+ # ergonomics), so they are listed by name next to the
120
+ # namespaced marketplace commands they belong with.
121
+ if cmd.startswith("marketplace:")
122
+ or cmd in ("login", "logout", "whoami")
123
+ ],
112
124
  "🧱 Database": [cmd for cmd in all_cmds if cmd.startswith("db:")],
113
125
  "💾 Cache": [cmd for cmd in all_cmds if cmd.startswith("cache:")],
114
126
  "🔍 Checks": [cmd for cmd in all_cmds if cmd.startswith("check:")],
@@ -0,0 +1,212 @@
1
+ """
2
+ check:contracts — Verify that feature contracts and SPL UVL models agree.
3
+
4
+ The dependency truth must not be split: a feature's requires.features (in
5
+ [tool.splent.contract]) and the SPL's UVL implications (A => B) describe
6
+ the same reality from two sides. This check reports:
7
+
8
+ - contract dependencies with no matching UVL constraint (ERROR: the SPL
9
+ would let a product select A without B and break at runtime), and
10
+ - UVL implications not backed by any contract (WARNING: the dependency
11
+ is invisible to feature:install and the marketplace).
12
+ """
13
+
14
+ from pathlib import Path
15
+
16
+ import click
17
+ import tomllib
18
+
19
+ from splent_cli.services import context, marketplace, spl_store
20
+
21
+
22
+ def _snapshot_version_key(pyproject_path: Path) -> tuple:
23
+ """Semver sort key for a cache snapshot dir like splent_feature_x@v1.10.0."""
24
+ import re
25
+
26
+ _, _, version = pyproject_path.parent.name.partition("@")
27
+ m = re.match(r"v?(\d+)\.(\d+)\.(\d+)", version)
28
+ if not m:
29
+ return (-1, -1, -1)
30
+ return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
31
+
32
+
33
+ def _local_contract_requires(workspace: str, package: str) -> list[str] | None:
34
+ """requires.features of a feature found locally (workspace root, else the
35
+ newest cache snapshot by semver). None when not available locally."""
36
+ candidates = [Path(workspace) / package / "pyproject.toml"]
37
+ cache = Path(workspace) / ".splent_cache" / "features"
38
+ if cache.is_dir():
39
+ candidates.extend(
40
+ sorted(
41
+ cache.glob(f"*/{package}@*/pyproject.toml"),
42
+ key=_snapshot_version_key,
43
+ reverse=True,
44
+ )
45
+ )
46
+
47
+ for path in candidates:
48
+ if not path.is_file():
49
+ continue
50
+ try:
51
+ data = tomllib.loads(path.read_text())
52
+ except (OSError, tomllib.TOMLDecodeError):
53
+ continue
54
+ requires = (
55
+ data.get("tool", {})
56
+ .get("splent", {})
57
+ .get("contract", {})
58
+ .get("requires", {})
59
+ )
60
+ return {
61
+ "features": requires.get("features", []),
62
+ "optional": requires.get("features_optional", []),
63
+ }
64
+ return None
65
+
66
+
67
+ @click.command(
68
+ "check:contracts",
69
+ short_help="Check that feature contracts and SPL UVL constraints agree.",
70
+ )
71
+ @click.option("--spl", "spl_name", default=None, help="Check a single SPL.")
72
+ def check_contracts(spl_name):
73
+ """
74
+ Cross-check requires.features in every contract against the UVL
75
+ constraints of every SPL model this workspace knows.
76
+
77
+ \b
78
+ Examples:
79
+ splent check:contracts
80
+ splent check:contracts --spl cms_spl
81
+ """
82
+ workspace = str(context.workspace())
83
+
84
+ if spl_name:
85
+ spl_names = [spl_name]
86
+ else:
87
+ spl_names = spl_store.known_spls(workspace)
88
+
89
+ if not spl_names:
90
+ click.secho(" No SPL models known to this workspace.", fg="red")
91
+ click.echo(" Start one with: splent spl:create <name>")
92
+ raise SystemExit(1)
93
+
94
+ total_errors = 0
95
+ total_warnings = 0
96
+
97
+ for name in spl_names:
98
+ spl_errors = 0
99
+ spl_warnings = 0
100
+ pin = spl_store.read_pin(workspace, name)
101
+ uvl_path_str = spl_store.find_uvl(
102
+ workspace, name, version=pin.version, doi=pin.doi
103
+ )
104
+ click.echo()
105
+ click.echo(click.style(f" check:contracts {name}", bold=True))
106
+ click.echo(click.style(f" {'-' * 60}", fg="bright_black"))
107
+
108
+ if not uvl_path_str:
109
+ if pin.fetchable:
110
+ click.secho(
111
+ f" UVL not on disk, run: splent spl:fetch {name}",
112
+ fg="yellow",
113
+ )
114
+ else:
115
+ click.secho(
116
+ f" No UVL and no DOI for '{name}', nothing to check against.",
117
+ fg="yellow",
118
+ )
119
+ total_warnings += 1
120
+ continue
121
+
122
+ uvl_path = Path(uvl_path_str)
123
+ model = marketplace.parse_uvl_structure(uvl_path.read_text())
124
+ shorts = model["features"] # short -> {package, org, ...}
125
+ implications = {(a, b) for a, b in model["constraints"]}
126
+
127
+ contracts: dict[str, list[str] | None] = {}
128
+ for short, meta in shorts.items():
129
+ contracts[short] = _local_contract_requires(workspace, meta["package"])
130
+
131
+ missing_local = sorted(s for s, req in contracts.items() if req is None)
132
+
133
+ # 1. Contract deps that the UVL does not enforce.
134
+ for short, req in sorted(contracts.items()):
135
+ for dep in (req or {}).get("features", []):
136
+ if dep not in shorts:
137
+ # Dependency on a feature outside this SPL: the SPL simply
138
+ # cannot offer this feature safely without it.
139
+ click.secho(
140
+ f" ✗ {short} requires '{dep}' (contract), but '{dep}' "
141
+ f"is not a feature of {name}",
142
+ fg="red",
143
+ )
144
+ spl_errors += 1
145
+ elif (short, dep) not in implications:
146
+ click.secho(
147
+ f" ✗ {short} requires '{dep}' (contract), but the UVL "
148
+ f"has no '{short} => {dep}' constraint",
149
+ fg="red",
150
+ )
151
+ click.echo(
152
+ click.style(
153
+ f" fix: add '{short} => {dep}' to the UVL "
154
+ f"constraints (splent spl:add-constraints "
155
+ f"{name} is interactive)",
156
+ fg="bright_black",
157
+ )
158
+ )
159
+ spl_errors += 1
160
+
161
+ # 2. UVL implications with no contract behind them.
162
+ for a, b in sorted(implications):
163
+ req = contracts.get(a)
164
+ if a not in shorts or b not in shorts:
165
+ continue # constraint over infra features (redis, nginx…)
166
+ if req is None:
167
+ continue # feature not local — cannot verify
168
+ if b in req["optional"]:
169
+ continue # SPL hardening a soft (graceful) dependency — fine
170
+ if b not in req["features"]:
171
+ click.secho(
172
+ f" ⚠ UVL says '{a} => {b}' but {a}'s contract does not "
173
+ f"require '{b}'",
174
+ fg="yellow",
175
+ )
176
+ click.echo(
177
+ click.style(
178
+ f' fix: add "{b}" to requires.features_manual in '
179
+ f"{shorts[a]['package']}/pyproject.toml (preserved on "
180
+ "regeneration), or confirm it is SPL-level policy",
181
+ fg="bright_black",
182
+ )
183
+ )
184
+ spl_warnings += 1
185
+
186
+ if missing_local:
187
+ click.echo(
188
+ click.style(
189
+ f" ℹ not local (skipped): {', '.join(missing_local)}",
190
+ fg="bright_black",
191
+ )
192
+ )
193
+
194
+ if not spl_errors and not spl_warnings:
195
+ click.secho(" ✅ Contracts and UVL agree.", fg="green")
196
+ total_errors += spl_errors
197
+ total_warnings += spl_warnings
198
+
199
+ click.echo()
200
+ if total_errors or total_warnings:
201
+ click.echo(
202
+ click.style(
203
+ f" {total_errors} error(s), {total_warnings} warning(s).",
204
+ bold=True,
205
+ )
206
+ )
207
+ click.echo()
208
+ if total_errors:
209
+ raise SystemExit(1)
210
+
211
+
212
+ cli_command = check_contracts
@@ -196,31 +196,36 @@ def check_deps():
196
196
  product = context.require_app()
197
197
  product_dir = os.path.join(workspace, product)
198
198
 
199
- # Read UVL (catalog or legacy)
199
+ # Read the UVL: the named SPL model, or the legacy in-product file
200
+ from splent_cli.services import spl_store
201
+
200
202
  try:
201
203
  reader = PyprojectReader.for_product(product_dir)
202
- # 1. Catalog: [tool.splent].spl
203
204
  spl_name = reader.splent_config.get("spl")
204
205
  if spl_name:
205
- uvl_path = os.path.join(
206
- workspace, "splent_catalog", spl_name, f"{spl_name}.uvl"
207
- )
206
+ uvl_path = spl_store.product_uvl(workspace, product, allow_fetch=True)
207
+ if not uvl_path:
208
+ click.secho(
209
+ " [x] " + spl_store.missing_model_message(workspace, spl_name),
210
+ fg="red",
211
+ )
212
+ raise SystemExit(1)
208
213
  else:
209
214
  # 2. Legacy: [tool.splent.uvl].file
210
215
  uvl_file = reader.uvl_config.get("file")
211
216
  if not uvl_file:
212
217
  click.secho(
213
- " [✖] No UVL configured. Set [tool.splent].spl or [tool.splent.uvl].file.",
218
+ " [x] No UVL configured. Set [tool.splent].spl or [tool.splent.uvl].file.",
214
219
  fg="red",
215
220
  )
216
221
  raise SystemExit(1)
217
222
  uvl_path = os.path.join(product_dir, "uvl", uvl_file)
218
223
  except (FileNotFoundError, RuntimeError) as e:
219
- click.secho(f" [✖] Cannot read pyproject.toml: {e}", fg="red")
224
+ click.secho(f" [x] Cannot read pyproject.toml: {e}", fg="red")
220
225
  raise SystemExit(1)
221
226
 
222
227
  if not os.path.isfile(uvl_path):
223
- click.secho(f" [✖] UVL file not found: {uvl_path}", fg="red")
228
+ click.secho(f" [x] UVL file not found: {uvl_path}", fg="red")
224
229
  raise SystemExit(1)
225
230
 
226
231
  package_map, allowed_deps = _parse_uvl_deps(uvl_path)
@@ -0,0 +1,127 @@
1
+ import click
2
+
3
+ from splent_cli.services import registry, release_gate
4
+
5
+
6
+ @click.command(
7
+ "check:pypi",
8
+ short_help="Verify PyPI credentials from .env (TWINE_USERNAME / TWINE_PASSWORD)",
9
+ )
10
+ @click.option(
11
+ "--test", is_flag=True, default=False, help="Check against TestPyPI instead of PyPI"
12
+ )
13
+ def check_pypi(test: bool):
14
+ click.echo(click.style("\nPyPI Credentials Check\n", fg="cyan", bold=True))
15
+
16
+ # Exactly the credentials the upload will use, resolved once, so this
17
+ # command and the release gate can never disagree about what is configured.
18
+ resolved_user, resolved_password = release_gate.pypi_credentials()
19
+ username = (resolved_user or "").strip()
20
+ password = (resolved_password or "").strip()
21
+
22
+ registry_name = "TestPyPI" if test else "PyPI"
23
+
24
+ # --- presence checks ---
25
+ if not username:
26
+ click.echo(click.style("[✖] ", fg="red") + "TWINE_USERNAME not set in .env")
27
+ click.secho(
28
+ " Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
29
+ )
30
+ raise SystemExit(1)
31
+ click.echo(click.style("[✔] ", fg="green") + f"TWINE_USERNAME = {username}")
32
+
33
+ if not password:
34
+ click.echo(
35
+ click.style("[✖] ", fg="red")
36
+ + "TWINE_PASSWORD (or PYPI_PASSWORD) not set in .env"
37
+ )
38
+ click.secho(
39
+ " Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
40
+ )
41
+ raise SystemExit(1)
42
+
43
+ masked = (
44
+ password[:4] + "*" * (len(password) - 8) + password[-4:]
45
+ if len(password) > 8
46
+ else "****"
47
+ )
48
+ click.echo(click.style("[✔] ", fg="green") + f"TWINE_PASSWORD = {masked}")
49
+
50
+ if username == "__token__" and not password.startswith("pypi-"):
51
+ click.echo(
52
+ click.style("[⚠] ", fg="yellow")
53
+ + "TWINE_USERNAME is '__token__' but password doesn't start with 'pypi-', may be invalid"
54
+ )
55
+ elif username == "__token__":
56
+ click.echo(
57
+ click.style("[✔] ", fg="green") + "Token format looks correct (pypi-...)"
58
+ )
59
+
60
+ # --- verify via the upload endpoint (valid token -> 400, invalid -> 403/401) ---
61
+ # The probe itself lives in services/registry.py, the single PyPI boundary,
62
+ # so the release gate and this command read the same signal from the same
63
+ # request instead of two probes that can disagree.
64
+ click.echo(f"\nContacting {registry_name} API...")
65
+ try:
66
+ probe = registry.pypi_upload_probe(username, password, test=test)
67
+ except registry.RegistryError as e:
68
+ click.echo(click.style("[✖] ", fg="red") + f"{e}")
69
+ raise SystemExit(1)
70
+
71
+ if probe.rate_limited:
72
+ click.echo(
73
+ click.style("[✖] ", fg="red")
74
+ + f"{registry_name} is RATE LIMITING this account (HTTP 429)"
75
+ )
76
+ click.secho(f" {release_gate.pypi_window(probe.retry_after)}", fg="yellow")
77
+ click.secho(
78
+ " The credentials are not the problem. Uploads will be refused until "
79
+ "the limit clears.",
80
+ fg="bright_black",
81
+ )
82
+ raise SystemExit(1)
83
+
84
+ if probe.status in (401, 403):
85
+ click.echo(
86
+ click.style("[✖] ", fg="red")
87
+ + f"Credentials rejected by {registry_name} (HTTP {probe.status}), "
88
+ "token invalid or expired"
89
+ )
90
+ click.secho(
91
+ " Run 'splent tokens:setup' for instructions on obtaining a valid token.",
92
+ fg="bright_black",
93
+ )
94
+ raise SystemExit(1)
95
+
96
+ if probe.status == 400:
97
+ click.echo(
98
+ click.style("[✔] ", fg="green")
99
+ + f"Credentials valid ({registry_name} returned 400, expected for an empty upload)"
100
+ )
101
+ elif 200 <= probe.status < 300:
102
+ click.echo(
103
+ click.style("[✔] ", fg="green") + f"Credentials accepted by {registry_name}"
104
+ )
105
+ else:
106
+ # An answer that is not a verdict is not a pass. The release gate
107
+ # refuses on this exact input, and the command an operator runs to
108
+ # decide whether to release must not be the more permissive of the two.
109
+ click.echo(
110
+ click.style("[✖] ", fg="red")
111
+ + f"{registry_name} returned HTTP {probe.status}, which is not a usable "
112
+ "answer, so the credentials could NOT be confirmed"
113
+ )
114
+ if probe.detail:
115
+ click.secho(f" {probe.detail}", fg="bright_black")
116
+ click.secho(
117
+ " Retry once it answers normally. A release would be refused in this "
118
+ "state.",
119
+ fg="yellow",
120
+ )
121
+ raise SystemExit(1)
122
+
123
+ click.echo()
124
+ click.secho(f"PyPI credentials OK ({registry_name}).", fg="green")
125
+
126
+
127
+ cli_command = check_pypi
@@ -128,14 +128,24 @@ def check_pyproject():
128
128
  # SPL / UVL
129
129
  spl_name = splent.get("spl")
130
130
  if spl_name:
131
- uvl_path = os.path.join(
132
- workspace, "splent_catalog", spl_name, f"{spl_name}.uvl"
131
+ from splent_cli.services import spl_store
132
+
133
+ pin = spl_store.read_pin(workspace, spl_name, product=product)
134
+ uvl_path = spl_store.find_uvl(
135
+ workspace, spl_name, version=pin.version, doi=pin.doi
133
136
  )
134
- if os.path.isfile(uvl_path):
135
- _ok(f"SPL catalog: {spl_name} (UVL found)")
137
+ if uvl_path:
138
+ _ok(f"SPL model {spl_name} (UVL found at {uvl_path})")
139
+ elif pin.fetchable:
140
+ _warn(
141
+ f"SPL model {spl_name} not downloaded yet, run: "
142
+ f"splent spl:fetch {spl_name}"
143
+ )
136
144
  else:
137
145
  _fail(
138
- f"SPL catalog: {spl_name} — UVL not found at splent_catalog/{spl_name}/{spl_name}.uvl"
146
+ f"SPL model {spl_name} has no UVL on disk and no DOI recorded. "
147
+ f"Add [tool.splent.spl_model] with the DOI, or run: "
148
+ f"splent spl:create {spl_name}"
139
149
  )
140
150
  else:
141
151
  # Legacy fallback