splent-cli 1.22.2__tar.gz → 1.24.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 (196) hide show
  1. {splent_cli-1.22.2/src/splent_cli.egg-info → splent_cli-1.24.0}/PKG-INFO +4 -1
  2. {splent_cli-1.22.2 → splent_cli-1.24.0}/pyproject.toml +10 -1
  3. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/cli.py +14 -1
  4. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_contracts.py +24 -6
  5. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_env.py +7 -4
  6. splent_cli-1.24.0/src/splent_cli/commands/check/check_github.py +126 -0
  7. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_infra.py +21 -5
  8. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_migrate.py +34 -14
  9. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_status.py +4 -2
  10. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/export/export_puml.py +1 -1
  11. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_contract.py +11 -0
  12. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_fork.py +30 -14
  13. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_impact.py +1 -1
  14. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_refine.py +115 -23
  15. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_release.py +120 -7
  16. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_test.py +92 -1
  17. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_unlock.py +9 -3
  18. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_index.py +1 -1
  19. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_build.py +19 -0
  20. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_configure.py +1 -1
  21. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_containers.py +4 -1
  22. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_create.py +4 -4
  23. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deploy.py +21 -19
  24. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_derive.py +31 -7
  25. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_down.py +12 -4
  26. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_release.py +14 -1
  27. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_shell.py +21 -1
  28. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_core.py +10 -1
  29. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_resume.py +29 -2
  30. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_feature.py +15 -6
  31. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/tokens_setup.py +35 -11
  32. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/compose.py +12 -2
  33. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/index_refresh.py +54 -24
  34. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/preflight.py +28 -15
  35. splent_cli-1.24.0/src/splent_cli/services/pypi_quota.py +538 -0
  36. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/registry.py +118 -58
  37. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/release.py +196 -80
  38. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/release_gate.py +62 -33
  39. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/spl_store.py +33 -3
  40. splent_cli-1.24.0/src/splent_cli/utils/forge.py +231 -0
  41. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/git_url.py +47 -28
  42. {splent_cli-1.22.2 → splent_cli-1.24.0/src/splent_cli.egg-info}/PKG-INFO +4 -1
  43. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli.egg-info/SOURCES.txt +2 -0
  44. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli.egg-info/requires.txt +3 -0
  45. splent_cli-1.22.2/src/splent_cli/commands/check/check_github.py +0 -105
  46. {splent_cli-1.22.2 → splent_cli-1.24.0}/LICENSE +0 -0
  47. {splent_cli-1.22.2 → splent_cli-1.24.0}/README.md +0 -0
  48. {splent_cli-1.22.2 → splent_cli-1.24.0}/setup.cfg +0 -0
  49. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/__init__.py +0 -0
  50. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/__main__.py +0 -0
  51. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/__init__.py +0 -0
  52. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/__init__.py +0 -0
  53. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
  54. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
  55. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
  56. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
  57. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
  58. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
  59. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
  60. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
  61. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/__init__.py +0 -0
  62. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_deps.py +0 -0
  63. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_docker.py +0 -0
  64. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_features.py +0 -0
  65. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_product.py +0 -0
  66. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
  67. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
  68. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_releases.py +0 -0
  69. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/clear/__init__.py +0 -0
  70. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
  71. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
  72. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
  73. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/command/__init__.py +0 -0
  74. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/command/command_create.py +0 -0
  75. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/coverage.py +0 -0
  76. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_console.py +0 -0
  77. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_dump.py +0 -0
  78. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_reset.py +0 -0
  79. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_restore.py +0 -0
  80. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
  81. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_seed.py +0 -0
  82. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
  83. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/doctor.py +0 -0
  84. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_list.py +0 -0
  85. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_set.py +0 -0
  86. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_show.py +0 -0
  87. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/export/__init__.py +0 -0
  88. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
  89. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
  90. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
  91. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
  92. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
  93. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
  94. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
  95. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
  96. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
  97. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
  98. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
  99. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
  100. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
  101. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
  102. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
  103. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
  104. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
  105. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
  106. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
  107. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
  108. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
  109. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
  110. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
  111. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
  112. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
  113. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
  114. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
  115. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
  116. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
  117. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
  118. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
  119. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
  120. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
  121. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
  122. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/lint.py +0 -0
  123. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/locust.py +0 -0
  124. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/login.py +0 -0
  125. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
  126. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
  127. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
  128. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/__init__.py +0 -0
  129. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
  130. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_clean.py +0 -0
  131. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_commands.py +0 -0
  132. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_config.py +0 -0
  133. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_console.py +0 -0
  134. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
  135. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_drift.py +0 -0
  136. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_env.py +0 -0
  137. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_list.py +0 -0
  138. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_logs.py +0 -0
  139. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_missing.py +0 -0
  140. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_port.py +0 -0
  141. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
  142. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_restart.py +0 -0
  143. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_routes.py +0 -0
  144. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_run.py +0 -0
  145. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_select.py +0 -0
  146. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_signals.py +0 -0
  147. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
  148. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_test.py +0 -0
  149. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_up.py +0 -0
  150. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_validate.py +0 -0
  151. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/release/__init__.py +0 -0
  152. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/selenium.py +0 -0
  153. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/__init__.py +0 -0
  154. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
  155. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
  156. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
  157. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
  158. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
  159. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
  160. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
  161. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
  162. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
  163. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_migrate_catalog.py +0 -0
  164. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
  165. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
  166. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
  167. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
  168. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
  169. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
  170. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/commands/version.py +0 -0
  171. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/__init__.py +0 -0
  172. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/context.py +0 -0
  173. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/credentials.py +0 -0
  174. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/marketplace.py +0 -0
  175. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_api.py +0 -0
  176. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_url.py +0 -0
  177. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/__init__.py +0 -0
  178. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/archetype.py +0 -0
  179. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/cache_utils.py +0 -0
  180. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/command_loader.py +0 -0
  181. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/contract_freshness.py +0 -0
  182. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/db_utils.py +0 -0
  183. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/decorators.py +0 -0
  184. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
  185. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/feature_installer.py +0 -0
  186. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/feature_utils.py +0 -0
  187. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/integrity.py +0 -0
  188. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/io_utils.py +0 -0
  189. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/lifecycle.py +0 -0
  190. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/manifest.py +0 -0
  191. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/path_utils.py +0 -0
  192. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/proc.py +0 -0
  193. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli/utils/template_drift.py +0 -0
  194. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
  195. {splent_cli-1.22.2 → splent_cli-1.24.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
  196. {splent_cli-1.22.2 → splent_cli-1.24.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.22.2
3
+ Version: 1.24.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
@@ -9,9 +9,12 @@ Requires-Python: >=3.13
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
11
  Requires-Dist: click==8.1.8
12
+ Requires-Dist: jinja2>=3.1
12
13
  Requires-Dist: packaging>=24.0
14
+ Requires-Dist: python-dotenv>=1.0
13
15
  Requires-Dist: pyyaml>=6.0
14
16
  Requires-Dist: requests>=2.31.0
17
+ Requires-Dist: splent_framework<2,>=1.15.0
15
18
  Requires-Dist: tomli_w>=1.0.0
16
19
  Provides-Extra: dev
17
20
  Requires-Dist: setuptools==80.3.1; extra == "dev"
@@ -4,18 +4,27 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splent_cli"
7
- version = "1.22.2"
7
+ version = "1.24.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"
11
11
  authors = [{ name = "DiversoLab", email = "diversolab@us.es" }]
12
12
  license-files = ["LICENSE"]
13
13
 
14
+ # Everything the CLI imports at module level must be listed here (or come
15
+ # with splent_framework, see tests/unit/test_declared_dependencies.py):
16
+ # startup imports every command module, so one undeclared import breaks
17
+ # `splent --help` on a clean `pip install splent_cli`.
14
18
  dependencies = [
15
19
  "click==8.1.8",
20
+ "jinja2>=3.1",
16
21
  "packaging>=24.0",
22
+ "python-dotenv>=1.0",
17
23
  "pyyaml>=6.0",
18
24
  "requests>=2.31.0",
25
+ # The CLI drives the framework (paths, feature order, migrations, the
26
+ # app factory) and only works with the same major version of it.
27
+ "splent_framework>=1.15.0,<2",
19
28
  "tomli_w>=1.0.0",
20
29
  ]
21
30
 
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
 
3
4
  import click
@@ -37,6 +38,15 @@ class SPLENTCLI(click.Group):
37
38
  return self._feature_cmds_cache
38
39
 
39
40
  self._feature_cmds_cache: dict[str, click.BaseCommand] = {}
41
+ # Discovery runs before every command, including the ones a product
42
+ # that is not built yet (selected, not configured or derived) runs
43
+ # while getting there. Building its app then fails, and the framework
44
+ # logged that failure with a full traceback on every single command,
45
+ # although nothing was wrong. The failure is expected here and
46
+ # handled below, so the framework's logging is muted for the attempt.
47
+ debug = bool(os.getenv("SPLENT_DEBUG"))
48
+ if not debug:
49
+ logging.disable(logging.CRITICAL)
40
50
  try:
41
51
  from splent_cli.utils.dynamic_imports import get_app
42
52
 
@@ -57,10 +67,13 @@ class SPLENTCLI(click.Group):
57
67
  group.add_command(cmd)
58
68
  self._feature_cmds_cache[group.name] = group
59
69
  except Exception as e:
60
- if os.getenv("SPLENT_DEBUG"):
70
+ if debug:
61
71
  click.secho(
62
72
  f" ⚠ Feature commands not loaded: {e}", fg="yellow", err=True
63
73
  )
74
+ finally:
75
+ if not debug:
76
+ logging.disable(logging.NOTSET)
64
77
  return self._feature_cmds_cache
65
78
 
66
79
  def get_command(self, ctx, cmd_name):
@@ -17,6 +17,7 @@ import click
17
17
  import tomllib
18
18
 
19
19
  from splent_cli.services import context, marketplace, spl_store
20
+ from splent_cli.utils import forge
20
21
 
21
22
 
22
23
  def _snapshot_version_key(pyproject_path: Path) -> tuple:
@@ -65,7 +66,7 @@ def _local_contract_requires(workspace: str, package: str) -> list[str] | None:
65
66
 
66
67
 
67
68
  def _exists_remotely(org: str | None, package: str) -> bool:
68
- """Whether a feature not present locally at least exists on GitHub.
69
+ """Whether a feature not present locally at least exists on the forge.
69
70
 
70
71
  Answers True whenever the question cannot be settled: no org to look
71
72
  under, no network, a rate limit, anything unexpected. A diagnostic that
@@ -78,10 +79,27 @@ def _exists_remotely(org: str | None, package: str) -> bool:
78
79
  import urllib.error
79
80
  import urllib.request
80
81
 
81
- # The UVL writes the org the way GitHub spells it ("splent-io"), which is
82
- # not the way the filesystem does ("splent_io"), so it is used verbatim.
83
- url = f"https://github.com/{org}/{package}"
84
- request = urllib.request.Request(url, method="HEAD")
82
+ # The UVL writes the org the way the forge spells it ("splent-io"), which
83
+ # is not the way the filesystem does ("splent_io"), so it is used verbatim.
84
+ #
85
+ # GitHub keeps the anonymous HEAD against the web URL it has always used.
86
+ # A self-hosted forge is far likelier to keep its repositories private, and
87
+ # a private repository answers 404 to a stranger, which would read here as
88
+ # a feature nobody ever wrote. When such a forge gives us a token we ask
89
+ # its API instead, with GET because Gitea-compatible routes are registered
90
+ # for GET alone and answer a HEAD with 405.
91
+ token = None if forge.is_github() else forge.token()
92
+ if token:
93
+ request = urllib.request.Request(
94
+ f"{forge.api_base()}/repos/{org}/{package}",
95
+ headers={
96
+ "Authorization": f"token {token}",
97
+ "Accept": "application/json",
98
+ "User-Agent": "splent-cli",
99
+ },
100
+ )
101
+ else:
102
+ request = urllib.request.Request(forge.web_url(org, package), method="HEAD")
85
103
  try:
86
104
  with urllib.request.urlopen(request, timeout=10) as response:
87
105
  return response.status < 400
@@ -230,7 +248,7 @@ def check_contracts(spl_name):
230
248
  click.secho(
231
249
  f" ✗ {short} is offered by the model but does not exist: "
232
250
  f"{meta.get('org')}/{meta['package']} is not in this workspace, "
233
- f"not in the cache and not on GitHub",
251
+ f"not in the cache and not on {forge.host()}",
234
252
  fg="red",
235
253
  )
236
254
  click.echo(
@@ -8,6 +8,8 @@ import sys
8
8
  import click
9
9
  import importlib.metadata
10
10
 
11
+ from splent_cli.utils import forge
12
+
11
13
 
12
14
  def _pkg_version(name: str) -> str | None:
13
15
  try:
@@ -88,11 +90,12 @@ def check_env():
88
90
  f"CLI={'?' if not cli_v else cli_v} / Framework={'?' if not fw_v else fw_v}"
89
91
  )
90
92
 
91
- # Credentials
92
- if os.getenv("GITHUB_TOKEN"):
93
- _ok("GITHUB_TOKEN set")
93
+ # Credentials. The forge token is named after the forge in use: asking for
94
+ # GITHUB_TOKEN on a git.splent.io workspace sent people to the wrong place.
95
+ if forge.token():
96
+ _ok(f"{forge.token_env_name()} set ({forge.host()})")
94
97
  else:
95
- _warn("GITHUB_TOKEN not set")
98
+ _warn(f"{forge.token_env_name()} not set ({forge.host()})")
96
99
 
97
100
  if os.getenv("TWINE_PASSWORD") or os.getenv("PYPI_TOKEN"):
98
101
  _ok("PyPI token set")
@@ -0,0 +1,126 @@
1
+ import os
2
+ import urllib.request
3
+ import urllib.error
4
+ import json
5
+ import click
6
+
7
+ from splent_cli.utils import forge
8
+
9
+
10
+ @click.command(
11
+ "check:github",
12
+ short_help="Verify forge credentials from .env (GITHUB_USER / forge token)",
13
+ )
14
+ def check_github():
15
+ # The command keeps its name, but the forge it checks is whatever the
16
+ # workspace is configured against: github.com by default, a self-hosted
17
+ # Forgejo when SPLENT_FORGE_HOST says so.
18
+ forge_host = forge.host()
19
+ token_var = forge.token_env_name()
20
+
21
+ click.echo(click.style(f"\n{forge_host} Credentials Check\n", fg="cyan", bold=True))
22
+
23
+ user = os.getenv("GITHUB_USER", "").strip()
24
+ # Resolved through forge so a self-hosted forge reads SPLENT_FORGE_TOKEN
25
+ # while GitHub keeps reading GITHUB_TOKEN, as it always has.
26
+ token = (forge.token() or "").strip()
27
+
28
+ # --- presence checks ---
29
+ if not user:
30
+ click.echo(click.style("[✖] ", fg="red") + "GITHUB_USER not set in .env")
31
+ click.secho(
32
+ " Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
33
+ )
34
+ raise SystemExit(1)
35
+ click.echo(click.style("[✔] ", fg="green") + f"GITHUB_USER = {user}")
36
+
37
+ if not token:
38
+ click.echo(click.style("[✖] ", fg="red") + f"{token_var} not set in .env")
39
+ click.secho(
40
+ " Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
41
+ )
42
+ raise SystemExit(1)
43
+
44
+ masked = token[:4] + "*" * (len(token) - 8) + token[-4:]
45
+ click.echo(click.style("[✔] ", fg="green") + f"{token_var} = {masked}")
46
+
47
+ # --- API call ---
48
+ # Vendor media types are GitHub's own; a Gitea-compatible forge does not
49
+ # know them and answers plain JSON, so they are only sent to GitHub.
50
+ headers = {
51
+ "Authorization": f"token {token}",
52
+ "Accept": "application/json",
53
+ "User-Agent": "splent-cli",
54
+ }
55
+ if forge.is_github():
56
+ headers["Accept"] = "application/vnd.github+json"
57
+ headers["X-GitHub-Api-Version"] = "2022-11-28"
58
+
59
+ click.echo(f"\nContacting the {forge_host} API...")
60
+ req = urllib.request.Request(f"{forge.api_base()}/user", headers=headers)
61
+
62
+ try:
63
+ with urllib.request.urlopen(req, timeout=10) as resp:
64
+ data = json.loads(resp.read().decode())
65
+ rate_remaining = resp.headers.get("X-RateLimit-Remaining", "?")
66
+ rate_limit = resp.headers.get("X-RateLimit-Limit", "?")
67
+ except urllib.error.HTTPError as e:
68
+ if e.code == 401:
69
+ click.echo(
70
+ click.style("[✖] ", fg="red")
71
+ + "Token invalid or expired (401 Unauthorized)"
72
+ )
73
+ click.secho(
74
+ " Run 'splent tokens:setup' for instructions on obtaining a valid token.",
75
+ fg="bright_black",
76
+ )
77
+ elif e.code == 403:
78
+ click.echo(
79
+ click.style("[✖] ", fg="red")
80
+ + "Token lacks required permissions (403 Forbidden)"
81
+ )
82
+ scope = "repo" if forge.is_github() else "write:repository"
83
+ click.secho(
84
+ f" Run 'splent tokens:setup', the token needs the '{scope}' scope.",
85
+ fg="bright_black",
86
+ )
87
+ else:
88
+ click.echo(
89
+ click.style("[✖] ", fg="red") + f"{forge_host} API error: HTTP {e.code}"
90
+ )
91
+ raise SystemExit(1)
92
+ except urllib.error.URLError as e:
93
+ click.echo(click.style("[✖] ", fg="red") + f"Network error: {e.reason}")
94
+ raise SystemExit(1)
95
+
96
+ # --- validate identity ---
97
+ api_login = data.get("login", "")
98
+ if api_login.lower() != user.lower():
99
+ click.echo(
100
+ click.style("[⚠] ", fg="yellow")
101
+ + f"Token belongs to '{api_login}', but GITHUB_USER is '{user}'"
102
+ )
103
+ else:
104
+ click.echo(click.style("[✔] ", fg="green") + f"Authenticated as {api_login}")
105
+
106
+ # GitHub calls the display name "name"; a Gitea-compatible forge calls it
107
+ # "full_name", so without the fallback every Forgejo check reported "-".
108
+ name = data.get("name") or data.get("full_name") or "-"
109
+ click.echo(click.style("[✔] ", fg="green") + f"Name: {name}")
110
+
111
+ # The plan and the rate limit headers are GitHub's alone. A Gitea-compatible
112
+ # forge answers /user without either, and printing "-" and "?/? remaining"
113
+ # would read as a fault rather than as an absence.
114
+ if forge.is_github():
115
+ plan = (data.get("plan") or {}).get("name", "-")
116
+ click.echo(click.style("[✔] ", fg="green") + f"Plan: {plan}")
117
+ click.echo(
118
+ click.style("[✔] ", fg="green")
119
+ + f"Rate limit: {rate_remaining}/{rate_limit} remaining"
120
+ )
121
+
122
+ click.echo()
123
+ click.secho(f"Credentials for {forge_host} OK.", fg="green")
124
+
125
+
126
+ cli_command = check_github
@@ -382,17 +382,33 @@ def check_infra():
382
382
  else:
383
383
  _ok(f"No port conflicts ({len(all_ports)} ports declared)")
384
384
 
385
- # Check against running containers (query docker ps once, not per-port)
385
+ # Check against running containers (query docker ps once, not per-port).
386
+ # The product's own stacks are not conflicts: with the product up, every
387
+ # declared port is held by exactly the container meant to hold it, and
388
+ # reporting those as warnings made a healthy product look broken.
389
+ own_projects = {compose.project_name(product, env)} | {
390
+ compose.feature_project_name(compose.normalize_feature_ref(f), product, env)
391
+ for f in features
392
+ }
386
393
  running_conflicts = []
387
- ps_result = _run(["docker", "ps", "--format", "{{.ID}}\t{{.Names}}\t{{.Ports}}"])
394
+ ps_result = _run(
395
+ [
396
+ "docker",
397
+ "ps",
398
+ "--format",
399
+ '{{.ID}}\t{{.Names}}\t{{.Label "com.docker.compose.project"}}\t{{.Ports}}',
400
+ ]
401
+ )
388
402
  if ps_result.returncode == 0:
389
403
  ps_lines = ps_result.stdout.splitlines()
390
404
  for port in all_ports:
391
405
  for line in ps_lines:
392
- parts = line.split("\t", 2)
393
- if len(parts) < 3:
406
+ parts = line.split("\t", 3)
407
+ if len(parts) < 4:
408
+ continue
409
+ cid, name, project, ports_str = parts
410
+ if project in own_projects:
394
411
  continue
395
- cid, name, ports_str = parts
396
412
  if f":{port}->" in ports_str:
397
413
  running_conflicts.append((port, name))
398
414
 
@@ -1,4 +1,6 @@
1
1
  import os
2
+ import shutil
3
+ import subprocess
2
4
 
3
5
  import click
4
6
  from flask_migrate import migrate as alembic_migrate
@@ -46,10 +48,23 @@ def _is_empty_migration(path: str) -> bool:
46
48
  return True
47
49
 
48
50
 
51
+ def _format_migration(path: str) -> None:
52
+ """Run ``ruff format`` on a freshly generated migration, if ruff exists.
53
+
54
+ Alembic writes single quotes and one long create_table() call, which the
55
+ release gate's ``ruff format --check`` then rejects: the first release of
56
+ any feature with a model failed lint on a file nobody wrote by hand.
57
+ """
58
+ ruff = shutil.which("ruff")
59
+ if not ruff:
60
+ return
61
+ subprocess.run([ruff, "format", "--quiet", path], check=False)
62
+
63
+
49
64
  @requires_db
50
65
  @click.command(
51
66
  "db:migrate",
52
- short_help="Generate a new migration from model changes and apply it.",
67
+ short_help="Generate a new migration from model changes (db:upgrade applies it).",
53
68
  )
54
69
  @click.argument("feature", required=False, default=None)
55
70
  @click.option(
@@ -67,8 +82,8 @@ def _is_empty_migration(path: str) -> bool:
67
82
  @context.requires_product
68
83
  def db_migrate(feature, message, empty):
69
84
  """
70
- Generate new migration files for features that have schema changes,
71
- then apply all pending migrations.
85
+ Generate new migration files for features that have schema changes.
86
+ Nothing is applied: run ``splent db:upgrade`` for that.
72
87
 
73
88
  Empty migrations (no schema changes detected) are automatically removed,
74
89
  unless ``--empty`` is passed, in which case a blank migration is created
@@ -158,10 +173,23 @@ def db_migrate(feature, message, empty):
158
173
 
159
174
  after = _count_versions(mdir)
160
175
 
176
+ newest = None
177
+ if after > before:
178
+ versions_dir = os.path.join(mdir, "versions")
179
+ newest = max(
180
+ (
181
+ os.path.join(versions_dir, f)
182
+ for f in os.listdir(versions_dir)
183
+ if f.endswith(".py")
184
+ ),
185
+ key=os.path.getmtime,
186
+ )
187
+
161
188
  # A blank migration requested with --empty is intentional: keep it so the
162
189
  # user can fill in upgrade()/downgrade() by hand (e.g. refinement columns).
163
190
  if empty:
164
- if after > before:
191
+ if newest:
192
+ _format_migration(newest)
165
193
  click.echo(
166
194
  click.style(
167
195
  f" 📝 {feat}: blank migration generated — "
@@ -174,20 +202,12 @@ def db_migrate(feature, message, empty):
174
202
  continue
175
203
 
176
204
  # If a new file was generated, check if it's empty (no real schema changes)
177
- if after > before:
178
- versions_dir = os.path.join(mdir, "versions")
179
- newest = max(
180
- (
181
- os.path.join(versions_dir, f)
182
- for f in os.listdir(versions_dir)
183
- if f.endswith(".py")
184
- ),
185
- key=os.path.getmtime,
186
- )
205
+ if newest:
187
206
  if _is_empty_migration(newest):
188
207
  os.remove(newest)
189
208
  click.echo(click.style(f" ✔ {feat}: up to date", fg="green"))
190
209
  else:
210
+ _format_migration(newest)
191
211
  click.echo(
192
212
  click.style(f" 📝 {feat}: new migration generated", fg="cyan")
193
213
  )
@@ -104,8 +104,10 @@ def db_status():
104
104
  mdir = dirs.get(feat)
105
105
  fs_head = _get_filesystem_head(mdir) if mdir else None
106
106
 
107
- db_display = db_rev[:12] if db_rev else "—"
108
- fs_display = fs_head[:12] if fs_head else "—"
107
+ # Cut to the column, not to 12: a hex revision is 12 characters but a
108
+ # named one is not, and "auth0002_role" showed as "auth0002_rol".
109
+ db_display = db_rev[:col_rev] if db_rev else "—"
110
+ fs_display = fs_head[:col_rev] if fs_head else "—"
109
111
 
110
112
  if db_rev and fs_head and db_rev == fs_head:
111
113
  status = click.style("✔ synced", fg="green")
@@ -817,7 +817,7 @@ def export_puml(
817
817
  if not uvl_path:
818
818
  click.secho(
819
819
  "❌ No UVL file found.\n"
820
- " Set [tool.splent].spl in pyproject.toml (SPL catalog)\n"
820
+ " Set [tool.splent].spl in pyproject.toml (the SPL model)\n"
821
821
  " or [tool.splent.uvl].file (legacy).",
822
822
  fg="red",
823
823
  )
@@ -233,6 +233,17 @@ def _print_diff(current: dict, inferred: dict) -> bool:
233
233
  diff_lines.append(click.style(f" - archetype: {old_arch}", fg="red"))
234
234
  diff_lines.append(click.style(f" + archetype: {new_arch}", fg="green"))
235
235
 
236
+ # extensible.routes is a boolean; a flip alone is still a change to write
237
+ old_routes = bool(current.get("extensible_routes", False))
238
+ new_routes = bool(inferred.get("extensible_routes", False))
239
+ if old_routes != new_routes:
240
+ diff_lines.append(
241
+ click.style(
242
+ f" ~ extensible.routes: {old_routes} → {new_routes}",
243
+ fg="green" if new_routes else "red",
244
+ )
245
+ )
246
+
236
247
  for key, label in FIELDS:
237
248
  diff_lines.extend(
238
249
  _diff_field(label, current.get(key, []), inferred.get(key, []))
@@ -5,32 +5,46 @@ import requests
5
5
  from splent_cli.commands.feature.feature_clone import feature_clone
6
6
  from splent_cli.utils.cache_utils import make_feature_writable
7
7
  from splent_cli.services import context
8
+ from splent_cli.utils import forge
8
9
  from splent_cli.utils.feature_utils import normalize_namespace
9
10
 
10
11
 
12
+ def _forge_label() -> str:
13
+ """How to name the forge in a message: the brand on GitHub, the host elsewhere."""
14
+ return "GitHub" if forge.is_github() else forge.host()
15
+
16
+
11
17
  @click.command(
12
18
  "feature:fork",
13
- short_help="Fork a feature on GitHub and clone it locally",
19
+ short_help="Fork a feature on the forge and clone it locally",
14
20
  )
15
21
  @click.argument("feature_name", required=True)
16
22
  @click.option(
17
23
  "--version", "-v", default="v1.0.0", help="Feature version (default: v1.0.0)"
18
24
  )
19
25
  def feature_fork(feature_name, version):
20
- token = os.getenv("GITHUB_TOKEN")
26
+ token = forge.token()
21
27
  github_user = os.getenv("GITHUB_USER")
22
28
 
23
29
  if not token or not github_user:
24
- click.secho("✖ Error: GITHUB_TOKEN and GITHUB_USER must be set", fg="red")
30
+ click.secho(
31
+ f"✖ Error: {forge.token_env_name()} and GITHUB_USER must be set", fg="red"
32
+ )
25
33
  raise SystemExit(1)
26
34
 
27
- upstream_owner = "splent-io"
28
- api_url = f"https://api.github.com/repos/{upstream_owner}/{feature_name}/forks"
35
+ upstream_owner = os.getenv("SPLENT_DEFAULT_ORG", "splent-io")
36
+ api_url = f"{forge.api_base()}/repos/{upstream_owner}/{feature_name}/forks"
29
37
  headers = {
30
38
  "Authorization": f"token {token}",
31
- "Accept": "application/vnd.github+json",
39
+ "Accept": (
40
+ "application/vnd.github+json" if forge.is_github() else "application/json"
41
+ ),
32
42
  }
33
43
 
44
+ # `default_branch_only` is a GitHub-only key. Gitea-compatible forges reject
45
+ # nothing but understand nothing either, so they get an empty body.
46
+ payload = {"default_branch_only": False} if forge.is_github() else {}
47
+
34
48
  click.secho(
35
49
  f"🔁 Forking {upstream_owner}/{feature_name} into {github_user}...", fg="cyan"
36
50
  )
@@ -38,17 +52,17 @@ def feature_fork(feature_name, version):
38
52
  resp = requests.post(
39
53
  api_url,
40
54
  headers=headers,
41
- json={"default_branch_only": False},
55
+ json=payload,
42
56
  timeout=30,
43
57
  )
44
58
  except requests.RequestException as e:
45
- click.secho(f"❌ Could not reach GitHub: {e}", fg="red")
59
+ click.secho(f"❌ Could not reach {_forge_label()}: {e}", fg="red")
46
60
  raise SystemExit(2)
47
61
 
48
62
  if resp.status_code in (403, 429):
49
63
  click.secho(
50
- "⚠ GitHub API rate limit or access denied. "
51
- "Set GITHUB_TOKEN to a token with repo scope and try again.",
64
+ f"⚠ {_forge_label()} API rate limit or access denied. "
65
+ f"Set {forge.token_env_name()} to a token with repo scope and try again.",
52
66
  fg="yellow",
53
67
  )
54
68
  if os.getenv("SPLENT_DEBUG"):
@@ -73,22 +87,24 @@ def feature_fork(feature_name, version):
73
87
  raise SystemExit(2)
74
88
  click.secho(f"🌐 Fork created: {fork_html_url}", fg="green")
75
89
 
76
- # Esperar a que GitHub lo procese
90
+ # Esperar a que la forja lo procese
77
91
  for i in range(5):
78
92
  try:
79
93
  r = requests.get(
80
- f"https://api.github.com/repos/{github_user}/{feature_name}",
94
+ f"{forge.api_base()}/repos/{github_user}/{feature_name}",
81
95
  headers=headers,
82
96
  timeout=30,
83
97
  )
84
98
  except requests.RequestException as e:
85
- click.secho(f"⌛ Waiting for GitHub to process fork... ({e})", fg="yellow")
99
+ click.secho(
100
+ f"⌛ Waiting for {_forge_label()} to process fork... ({e})", fg="yellow"
101
+ )
86
102
  time.sleep(3)
87
103
  continue
88
104
  if r.status_code == 200:
89
105
  click.secho("✅ Fork is ready.", fg="green")
90
106
  break
91
- click.secho("⌛ Waiting for GitHub to process fork...", fg="yellow")
107
+ click.secho(f"⌛ Waiting for {_forge_label()} to process fork...", fg="yellow")
92
108
  time.sleep(3)
93
109
  else:
94
110
  click.secho("⚠ Fork may still be processing.", fg="yellow")
@@ -65,7 +65,7 @@ def feature_impact(feature_ref):
65
65
  click.secho(" pyproject.toml not found.", fg="red")
66
66
  raise SystemExit(1)
67
67
 
68
- uvl = _uvl_path(product_dir)
68
+ uvl = _uvl_path(product_dir, workspace)
69
69
  if not uvl or not os.path.isfile(uvl):
70
70
  click.secho(" No UVL file found — cannot compute impact.", fg="red")
71
71
  raise SystemExit(1)