splent-cli 1.6.0__tar.gz → 1.7.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.
- {splent_cli-1.6.0/src/splent_cli.egg-info → splent_cli-1.7.0}/PKG-INFO +1 -1
- {splent_cli-1.6.0 → splent_cli-1.7.0}/pyproject.toml +1 -1
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_reset.py +4 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_upgrade.py +5 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_add.py +19 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_attach.py +21 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_env.py +12 -14
- splent_cli-1.7.0/src/splent_cli/commands/feature/feature_impact.py +161 -0
- splent_cli-1.7.0/src/splent_cli/commands/feature/feature_install.py +425 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_test.py +48 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_build.py +153 -11
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_configure.py +66 -3
- splent_cli-1.7.0/src/splent_cli/commands/product/product_containers.py +316 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_deploy.py +60 -6
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_env.py +33 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_restart.py +36 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_test.py +14 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_up.py +12 -10
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/services/preflight.py +70 -1
- {splent_cli-1.6.0 → splent_cli-1.7.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli.egg-info/SOURCES.txt +2 -0
- splent_cli-1.6.0/src/splent_cli/commands/product/product_containers.py +0 -196
- {splent_cli-1.6.0 → splent_cli-1.7.0}/LICENSE +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/README.md +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/setup.cfg +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_release.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_release.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/tokens_setup.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/services/release.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.0}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.6.0 → splent_cli-1.7.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.
|
|
3
|
+
Version: 1.7.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
|
|
@@ -123,6 +123,10 @@ def db_reset(yes):
|
|
|
123
123
|
name=name,
|
|
124
124
|
version=version,
|
|
125
125
|
)
|
|
126
|
+
except ImportError as e:
|
|
127
|
+
if "models" in str(e):
|
|
128
|
+
continue
|
|
129
|
+
click.echo(click.style(f" ❌ {feat}: {e}", fg="red"))
|
|
126
130
|
except Exception as e:
|
|
127
131
|
click.echo(click.style(f" ❌ {feat}: {e}", fg="red"))
|
|
128
132
|
|
|
@@ -84,6 +84,11 @@ def db_upgrade(feature):
|
|
|
84
84
|
name=name,
|
|
85
85
|
version=version,
|
|
86
86
|
)
|
|
87
|
+
except ImportError as e:
|
|
88
|
+
if "models" in str(e):
|
|
89
|
+
# Feature has migrations/ dir but no models module — skip silently
|
|
90
|
+
continue
|
|
91
|
+
click.echo(click.style(f" ❌ {feat}: {e}", fg="red"))
|
|
87
92
|
except Exception as e:
|
|
88
93
|
click.echo(click.style(f" ❌ {feat}: {e}", fg="red"))
|
|
89
94
|
|
|
@@ -59,6 +59,25 @@ def feature_add(full_name, env_scope):
|
|
|
59
59
|
)
|
|
60
60
|
raise SystemExit(1)
|
|
61
61
|
|
|
62
|
+
# ── Auto-detect env scope from feature contract ───────────────────
|
|
63
|
+
if not env_scope:
|
|
64
|
+
feat_pyproject = os.path.join(feature_dir, "pyproject.toml")
|
|
65
|
+
if os.path.isfile(feat_pyproject):
|
|
66
|
+
with open(feat_pyproject, "rb") as f:
|
|
67
|
+
feat_data = tomllib.load(f)
|
|
68
|
+
contract_env = (
|
|
69
|
+
feat_data.get("tool", {})
|
|
70
|
+
.get("splent", {})
|
|
71
|
+
.get("contract", {})
|
|
72
|
+
.get("env")
|
|
73
|
+
)
|
|
74
|
+
if contract_env:
|
|
75
|
+
env_scope = contract_env
|
|
76
|
+
click.echo(
|
|
77
|
+
click.style(" scope ", dim=True)
|
|
78
|
+
+ f"contract declares env={contract_env} → features_{contract_env}"
|
|
79
|
+
)
|
|
80
|
+
|
|
62
81
|
# ── Update pyproject.toml ─────────────────────────────────────────
|
|
63
82
|
pyproject_path = os.path.join(workspace, product, "pyproject.toml")
|
|
64
83
|
if not os.path.exists(pyproject_path):
|
|
@@ -64,6 +64,27 @@ def feature_attach(feature_identifier, version, env_scope):
|
|
|
64
64
|
|
|
65
65
|
short = feature_name.replace("splent_feature_", "")
|
|
66
66
|
|
|
67
|
+
# ── Auto-detect env scope from feature contract ───────────────────
|
|
68
|
+
if not env_scope:
|
|
69
|
+
feat_pyproject = os.path.join(versioned_dir, "pyproject.toml")
|
|
70
|
+
if os.path.isfile(feat_pyproject):
|
|
71
|
+
import tomllib as _tomllib
|
|
72
|
+
|
|
73
|
+
with open(feat_pyproject, "rb") as f:
|
|
74
|
+
feat_data = _tomllib.load(f)
|
|
75
|
+
contract_env = (
|
|
76
|
+
feat_data.get("tool", {})
|
|
77
|
+
.get("splent", {})
|
|
78
|
+
.get("contract", {})
|
|
79
|
+
.get("env")
|
|
80
|
+
)
|
|
81
|
+
if contract_env:
|
|
82
|
+
env_scope = contract_env
|
|
83
|
+
click.echo(
|
|
84
|
+
click.style(" scope ", dim=True)
|
|
85
|
+
+ f"contract declares env={contract_env} → features_{contract_env}"
|
|
86
|
+
)
|
|
87
|
+
|
|
67
88
|
# ── Update pyproject.toml ─────────────────────────────────────────
|
|
68
89
|
full_name = f"{namespace}/{feature_name}@{version}"
|
|
69
90
|
bare_name = f"{namespace}/{feature_name}"
|
|
@@ -45,19 +45,26 @@ def feature_env(feature_name, generate, env_name):
|
|
|
45
45
|
with open(pyproject_path, "rb") as f:
|
|
46
46
|
data = tomllib.load(f)
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
# 2️⃣ Determinar entorno (antes de leer features, para incluir features_dev/prod)
|
|
49
|
+
env_from_var = os.getenv("SPLENT_ENV")
|
|
50
|
+
if not env_name and env_from_var:
|
|
51
|
+
env_name = env_from_var
|
|
52
|
+
click.echo(f"🌍 Using SPLENT_ENV={env_name}")
|
|
53
|
+
elif not env_name:
|
|
54
|
+
click.echo("❌ You must specify --dev, --prod, or define SPLENT_ENV=dev|prod.")
|
|
55
|
+
raise SystemExit(1)
|
|
56
|
+
|
|
57
|
+
feature_entries = read_features_from_data(data, env_name)
|
|
49
58
|
if not feature_entries:
|
|
50
59
|
click.echo("❌ No features declared in pyproject.toml.")
|
|
51
60
|
raise SystemExit(1)
|
|
52
61
|
|
|
53
|
-
#
|
|
62
|
+
# 3️⃣ Buscar la feature pedida con su versión
|
|
54
63
|
feature_entry = next(
|
|
55
64
|
(f for f in feature_entries if f.startswith(feature_name)), None
|
|
56
65
|
)
|
|
57
66
|
if not feature_entry:
|
|
58
|
-
click.echo(
|
|
59
|
-
f"❌ Feature '{feature_name}' not found in [features] section of {pyproject_path}"
|
|
60
|
-
)
|
|
67
|
+
click.echo(f"❌ Feature '{feature_name}' not found in pyproject.toml")
|
|
61
68
|
raise SystemExit(1)
|
|
62
69
|
|
|
63
70
|
# Derive org_safe from the entry itself (e.g. "splent_io/splent_feature_auth@v1")
|
|
@@ -68,15 +75,6 @@ def feature_env(feature_name, generate, env_name):
|
|
|
68
75
|
org_safe = "splent_io"
|
|
69
76
|
entry_basename = feature_entry
|
|
70
77
|
|
|
71
|
-
# 3️⃣ Determinar entorno
|
|
72
|
-
env_from_var = os.getenv("SPLENT_ENV")
|
|
73
|
-
if not env_name and env_from_var:
|
|
74
|
-
env_name = env_from_var
|
|
75
|
-
click.echo(f"🌍 Using SPLENT_ENV={env_name}")
|
|
76
|
-
elif not env_name:
|
|
77
|
-
click.echo("❌ You must specify --dev, --prod, or define SPLENT_ENV=dev|prod.")
|
|
78
|
-
raise SystemExit(1)
|
|
79
|
-
|
|
80
78
|
# 4️⃣ Ruta del symlink del producto (con versión incluida)
|
|
81
79
|
symlink_path = os.path.join(
|
|
82
80
|
workspace, product, "features", org_safe, entry_basename
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
splent feature:impact
|
|
3
|
+
|
|
4
|
+
Shows the full dependency impact of a feature within the active product:
|
|
5
|
+
what it requires, what depends on it, and what would break if removed.
|
|
6
|
+
|
|
7
|
+
All dependency information is resolved from the product's UVL constraints.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
|
|
14
|
+
from splent_cli.services import context
|
|
15
|
+
from splent_cli.commands.feature.feature_order import (
|
|
16
|
+
_uvl_path,
|
|
17
|
+
_build_requires_map,
|
|
18
|
+
_build_reverse_map,
|
|
19
|
+
_closure,
|
|
20
|
+
_parse_entry,
|
|
21
|
+
)
|
|
22
|
+
from splent_framework.utils.pyproject_reader import PyprojectReader
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _short_name(pkg: str) -> str:
|
|
26
|
+
"""splent_feature_auth → auth"""
|
|
27
|
+
return pkg.removeprefix("splent_feature_")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.command(
|
|
31
|
+
"feature:impact",
|
|
32
|
+
short_help="Show the dependency impact of adding or removing a feature.",
|
|
33
|
+
)
|
|
34
|
+
@click.argument("feature_ref", required=True)
|
|
35
|
+
def feature_impact(feature_ref):
|
|
36
|
+
"""
|
|
37
|
+
Show what FEATURE_REF depends on and what depends on it.
|
|
38
|
+
|
|
39
|
+
Reads the product's UVL constraints to build a bidirectional dependency
|
|
40
|
+
graph and shows:
|
|
41
|
+
|
|
42
|
+
\b
|
|
43
|
+
1. Dependencies — features this one requires to work.
|
|
44
|
+
2. Dependents — features that require this one.
|
|
45
|
+
3. Impact — transitive set of features that would break
|
|
46
|
+
if this feature were removed.
|
|
47
|
+
|
|
48
|
+
FEATURE_REF can be a short name (auth) or full package name
|
|
49
|
+
(splent_feature_auth).
|
|
50
|
+
|
|
51
|
+
\b
|
|
52
|
+
Examples:
|
|
53
|
+
splent feature:impact auth
|
|
54
|
+
splent feature:impact splent_feature_mail
|
|
55
|
+
"""
|
|
56
|
+
product = context.require_app()
|
|
57
|
+
workspace = str(context.workspace())
|
|
58
|
+
product_dir = os.path.join(workspace, product)
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
reader = PyprojectReader.for_product(product_dir)
|
|
62
|
+
env = os.getenv("SPLENT_ENV")
|
|
63
|
+
features_raw = reader.features_for_env(env)
|
|
64
|
+
except FileNotFoundError:
|
|
65
|
+
click.secho(" pyproject.toml not found.", fg="red")
|
|
66
|
+
raise SystemExit(1)
|
|
67
|
+
|
|
68
|
+
uvl = _uvl_path(product_dir)
|
|
69
|
+
if not uvl or not os.path.isfile(uvl):
|
|
70
|
+
click.secho(" No UVL file found — cannot compute impact.", fg="red")
|
|
71
|
+
raise SystemExit(1)
|
|
72
|
+
|
|
73
|
+
# Normalize input: accept "auth" or "splent_feature_auth"
|
|
74
|
+
target = feature_ref
|
|
75
|
+
if target.startswith("splent_feature_"):
|
|
76
|
+
target = target[len("splent_feature_") :]
|
|
77
|
+
pkg_target = f"splent_feature_{target}"
|
|
78
|
+
|
|
79
|
+
# Build declared feature set
|
|
80
|
+
declared_pkgs = set()
|
|
81
|
+
for entry in features_raw:
|
|
82
|
+
_, name, _ = _parse_entry(entry)
|
|
83
|
+
declared_pkgs.add(name)
|
|
84
|
+
|
|
85
|
+
if pkg_target not in declared_pkgs:
|
|
86
|
+
click.secho(f" '{feature_ref}' is not declared in this product.", fg="red")
|
|
87
|
+
raise SystemExit(1)
|
|
88
|
+
|
|
89
|
+
# Build dependency graphs
|
|
90
|
+
requires_map = _build_requires_map(uvl)
|
|
91
|
+
reverse_map = _build_reverse_map(requires_map)
|
|
92
|
+
|
|
93
|
+
# Direct dependencies (what I need)
|
|
94
|
+
direct_deps = requires_map.get(pkg_target, [])
|
|
95
|
+
|
|
96
|
+
# Full dependency closure (transitive: what I need, and what they need, etc.)
|
|
97
|
+
all_deps = _closure(pkg_target, requires_map, declared_pkgs)
|
|
98
|
+
|
|
99
|
+
# Direct dependents (who needs me)
|
|
100
|
+
direct_dependents = reverse_map.get(pkg_target, [])
|
|
101
|
+
|
|
102
|
+
# Full reverse closure (transitive: who breaks if I'm removed)
|
|
103
|
+
all_affected = _closure(pkg_target, reverse_map, declared_pkgs)
|
|
104
|
+
|
|
105
|
+
# ── Output ────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
click.echo()
|
|
108
|
+
click.secho(f" feature:impact — {target}", bold=True)
|
|
109
|
+
click.echo(click.style(f" Product: {product}", fg="bright_black"))
|
|
110
|
+
click.echo(click.style(f" UVL: {os.path.basename(uvl)}", fg="bright_black"))
|
|
111
|
+
|
|
112
|
+
# Section 1: What I depend on
|
|
113
|
+
click.echo()
|
|
114
|
+
click.secho(" Depends on", bold=True)
|
|
115
|
+
|
|
116
|
+
if not all_deps:
|
|
117
|
+
click.echo(click.style(" (none) — this feature is independent", fg="green"))
|
|
118
|
+
else:
|
|
119
|
+
direct_set = set(direct_deps)
|
|
120
|
+
for dep in all_deps:
|
|
121
|
+
marker = "direct" if dep in direct_set else "transitive"
|
|
122
|
+
icon = "→" if marker == "direct" else " →"
|
|
123
|
+
color = "cyan" if marker == "direct" else "bright_black"
|
|
124
|
+
click.echo(
|
|
125
|
+
f" {icon} {click.style(_short_name(dep), fg=color)} ({marker})"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Section 2: Who depends on me + impact
|
|
129
|
+
click.echo()
|
|
130
|
+
click.secho(" Depended on by", bold=True)
|
|
131
|
+
|
|
132
|
+
if not all_affected:
|
|
133
|
+
click.echo(click.style(" (none) — safe to remove", fg="green"))
|
|
134
|
+
else:
|
|
135
|
+
direct_set = set(direct_dependents)
|
|
136
|
+
for dep in all_affected:
|
|
137
|
+
marker = "direct" if dep in direct_set else "transitive"
|
|
138
|
+
icon = "←" if marker == "direct" else " ←"
|
|
139
|
+
color = "red" if marker == "direct" else "yellow"
|
|
140
|
+
click.echo(
|
|
141
|
+
f" {icon} {click.style(_short_name(dep), fg=color)} ({marker})"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Section 3: Summary
|
|
145
|
+
click.echo()
|
|
146
|
+
if all_affected:
|
|
147
|
+
click.secho(
|
|
148
|
+
f" Removing {target} would break {len(all_affected)} feature(s).",
|
|
149
|
+
fg="red",
|
|
150
|
+
bold=True,
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
click.secho(
|
|
154
|
+
f" Removing {target} has no impact on other features.",
|
|
155
|
+
fg="green",
|
|
156
|
+
bold=True,
|
|
157
|
+
)
|
|
158
|
+
click.echo()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
cli_command = feature_impact
|