splent-cli 1.23.0__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.
- {splent_cli-1.23.0/src/splent_cli.egg-info → splent_cli-1.24.0}/PKG-INFO +4 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/pyproject.toml +10 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/cli.py +14 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_env.py +7 -4
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_infra.py +21 -5
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_migrate.py +34 -14
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_status.py +4 -2
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/export/export_puml.py +1 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_contract.py +11 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_impact.py +1 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_refine.py +115 -23
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_release.py +111 -5
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_test.py +92 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_index.py +1 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_configure.py +1 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_containers.py +4 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_create.py +4 -4
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deploy.py +21 -19
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_derive.py +31 -7
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_down.py +12 -4
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_release.py +14 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_shell.py +21 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_core.py +10 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/release_resume.py +29 -2
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_feature.py +15 -6
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/tokens_setup.py +4 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/compose.py +12 -2
- splent_cli-1.24.0/src/splent_cli/services/pypi_quota.py +538 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/registry.py +16 -10
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/release.py +130 -29
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/release_gate.py +42 -23
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/spl_store.py +33 -3
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/forge.py +11 -4
- {splent_cli-1.23.0 → splent_cli-1.24.0/src/splent_cli.egg-info}/PKG-INFO +4 -1
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/SOURCES.txt +1 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/requires.txt +3 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/LICENSE +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/README.md +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/setup.cfg +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_build.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_migrate_catalog.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/services/preflight.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.23.0 → splent_cli-1.24.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.23.0 → 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.
|
|
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.
|
|
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
|
|
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):
|
|
@@ -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
|
-
|
|
93
|
-
|
|
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("
|
|
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")
|
|
@@ -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(
|
|
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",
|
|
393
|
-
if len(parts) <
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
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
|
|
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, []))
|
|
@@ -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)
|
|
@@ -54,6 +54,53 @@ def _is_stub(content: str) -> bool:
|
|
|
54
54
|
return any(marker.lower() in lower for marker in _SCAFFOLD_MARKERS)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def _scaffold_renderings(path: str) -> set[str]:
|
|
58
|
+
"""What feature:create writes at *path*, for every feature type.
|
|
59
|
+
|
|
60
|
+
The refiner is created with feature:create right before the wizard runs,
|
|
61
|
+
so its models.py, services.py, repositories.py and __init__.py are exactly
|
|
62
|
+
the scaffold, yet none of them carries a marker. The wizard used to ask
|
|
63
|
+
whether to overwrite each one, default No, and keeping the old __init__.py
|
|
64
|
+
left an import of a service the wizard had just replaced, so the app no
|
|
65
|
+
longer booted. Comparing against the rendered scaffold recognises them
|
|
66
|
+
without stamping a marker into files developers then edit.
|
|
67
|
+
"""
|
|
68
|
+
from splent_cli.commands.feature.feature_create import (
|
|
69
|
+
FEATURE_TYPES,
|
|
70
|
+
_build_file_map,
|
|
71
|
+
setup_jinja_env,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
parts = os.path.normpath(path).split(os.sep)
|
|
75
|
+
if "src" not in parts:
|
|
76
|
+
return set()
|
|
77
|
+
at = len(parts) - 1 - parts[::-1].index("src")
|
|
78
|
+
if len(parts) < at + 4:
|
|
79
|
+
return set()
|
|
80
|
+
org_safe, feature_name = parts[at + 1], parts[at + 2]
|
|
81
|
+
rel = "/".join(parts[at + 3 :])
|
|
82
|
+
short_name = feature_name.removeprefix("splent_feature_")
|
|
83
|
+
ctx = {
|
|
84
|
+
"feature_name": feature_name,
|
|
85
|
+
"short_name": short_name,
|
|
86
|
+
"pascal_name": "".join(w.capitalize() for w in short_name.split("_")),
|
|
87
|
+
"org_safe": org_safe,
|
|
88
|
+
"feature_import": f"{org_safe}.{feature_name}",
|
|
89
|
+
"env_prefix": short_name.upper(),
|
|
90
|
+
"host_port_ref": "${" + short_name.upper() + "_HOST_PORT}",
|
|
91
|
+
}
|
|
92
|
+
env = setup_jinja_env()
|
|
93
|
+
rendered = set()
|
|
94
|
+
for feature_type in FEATURE_TYPES:
|
|
95
|
+
template = _build_file_map(feature_type, short_name)[0].get(rel)
|
|
96
|
+
if template:
|
|
97
|
+
try:
|
|
98
|
+
rendered.add(env.get_template(template).render(ctx).strip())
|
|
99
|
+
except Exception:
|
|
100
|
+
continue
|
|
101
|
+
return rendered
|
|
102
|
+
|
|
103
|
+
|
|
57
104
|
def _confirm_overwrite(path: str, label: str) -> bool:
|
|
58
105
|
"""Warn before overwriting a non-stub source file. Returns True if it is
|
|
59
106
|
safe to write (file absent, a stub, or the user confirmed)."""
|
|
@@ -61,7 +108,7 @@ def _confirm_overwrite(path: str, label: str) -> bool:
|
|
|
61
108
|
return True
|
|
62
109
|
with open(path, "r", encoding="utf-8") as f:
|
|
63
110
|
existing = f.read()
|
|
64
|
-
if _is_stub(existing):
|
|
111
|
+
if _is_stub(existing) or existing.strip() in _scaffold_renderings(path):
|
|
65
112
|
return True
|
|
66
113
|
click.echo()
|
|
67
114
|
click.secho(
|
|
@@ -301,17 +348,22 @@ class {mixin_name}:
|
|
|
301
348
|
with open(models_path, "r") as f:
|
|
302
349
|
content = f.read()
|
|
303
350
|
if mixin_name in content:
|
|
304
|
-
|
|
351
|
+
click.echo(
|
|
352
|
+
click.style(" kept: ", dim=True)
|
|
353
|
+
+ f"models.py ({mixin_name} already there)"
|
|
354
|
+
)
|
|
355
|
+
return False
|
|
305
356
|
|
|
306
357
|
# Warn before clobbering developer-authored models.py
|
|
307
358
|
if not _confirm_overwrite(models_path, "models.py"):
|
|
308
359
|
click.echo(click.style(" skipped: ", dim=True) + "models.py (kept)")
|
|
309
|
-
return
|
|
360
|
+
return False
|
|
310
361
|
|
|
311
362
|
# Overwrite — refinement features only need mixins, not db.Model classes
|
|
312
363
|
os.makedirs(os.path.dirname(models_path), exist_ok=True)
|
|
313
364
|
with open(models_path, "w") as f:
|
|
314
365
|
f.write(mixin_code)
|
|
366
|
+
return True
|
|
315
367
|
|
|
316
368
|
|
|
317
369
|
def _scaffold_service(
|
|
@@ -348,12 +400,16 @@ def _scaffold_service(
|
|
|
348
400
|
with open(services_path, "r") as f:
|
|
349
401
|
content = f.read()
|
|
350
402
|
if replacement_name in content:
|
|
351
|
-
|
|
403
|
+
click.echo(
|
|
404
|
+
click.style(" kept: ", dim=True)
|
|
405
|
+
+ f"services.py ({replacement_name} already there)"
|
|
406
|
+
)
|
|
407
|
+
return False
|
|
352
408
|
|
|
353
409
|
# Warn before clobbering developer-authored services.py
|
|
354
410
|
if not _confirm_overwrite(services_path, "services.py"):
|
|
355
411
|
click.echo(click.style(" skipped: ", dim=True) + "services.py (kept)")
|
|
356
|
-
return
|
|
412
|
+
return False
|
|
357
413
|
|
|
358
414
|
# Overwrite — refinement features only need replacement classes
|
|
359
415
|
os.makedirs(os.path.dirname(services_path), exist_ok=True)
|
|
@@ -370,6 +426,7 @@ def _scaffold_service(
|
|
|
370
426
|
"# Refinement features do not need their own repository.\n"
|
|
371
427
|
"# The base feature's repository is used via the service locator.\n"
|
|
372
428
|
)
|
|
429
|
+
return True
|
|
373
430
|
|
|
374
431
|
|
|
375
432
|
def _scaffold_init(
|
|
@@ -436,14 +493,23 @@ def _scaffold_init(
|
|
|
436
493
|
+ " return {}\n"
|
|
437
494
|
)
|
|
438
495
|
|
|
496
|
+
# Running the wizard again finds the file it wrote last time; asking
|
|
497
|
+
# whether to overwrite it with the very same text only invites a "no".
|
|
498
|
+
if os.path.isfile(init_path):
|
|
499
|
+
with open(init_path, "r", encoding="utf-8") as f:
|
|
500
|
+
if f.read().strip() == code.strip():
|
|
501
|
+
click.echo(click.style(" unchanged: ", dim=True) + "__init__.py")
|
|
502
|
+
return False
|
|
503
|
+
|
|
439
504
|
# Warn before clobbering a developer-authored __init__.py
|
|
440
505
|
if not _confirm_overwrite(init_path, "__init__.py"):
|
|
441
506
|
click.echo(click.style(" skipped: ", dim=True) + "__init__.py (kept)")
|
|
442
|
-
return
|
|
507
|
+
return False
|
|
443
508
|
|
|
444
509
|
os.makedirs(os.path.dirname(init_path), exist_ok=True)
|
|
445
510
|
with open(init_path, "w") as f:
|
|
446
511
|
f.write(code)
|
|
512
|
+
return True
|
|
447
513
|
|
|
448
514
|
|
|
449
515
|
def _update_env_py(
|
|
@@ -503,7 +569,9 @@ def _scaffold_hooks(
|
|
|
503
569
|
for hook in fill_hooks:
|
|
504
570
|
# notes.index.before_list → notes_index_before_list
|
|
505
571
|
func_name = hook.replace(".", "_")
|
|
506
|
-
|
|
572
|
+
# Slots pass their context (notes.index.note_extra passes the note),
|
|
573
|
+
# so a stub that takes nothing raised TypeError the first time it ran.
|
|
574
|
+
func_lines.append(f"def {func_name}(*args, **kwargs):")
|
|
507
575
|
func_lines.append(f' """Fill the {hook} slot."""')
|
|
508
576
|
func_lines.append(f' return render_template("hooks/{func_name}.html")')
|
|
509
577
|
func_lines.append("")
|
|
@@ -516,22 +584,41 @@ def _scaffold_hooks(
|
|
|
516
584
|
"\n\n" + "\n".join(func_lines) + "\n".join(register_lines) + "\n"
|
|
517
585
|
)
|
|
518
586
|
|
|
519
|
-
# Don't overwrite
|
|
587
|
+
# Don't overwrite a hooks.py that already registers something. The
|
|
588
|
+
# scaffold only mentions register_template_hook inside its docstring, so a
|
|
589
|
+
# plain substring test mistook it for real content, skipped the write and
|
|
590
|
+
# still reported the hooks as scaffolded.
|
|
520
591
|
if os.path.isfile(hooks_path):
|
|
521
592
|
with open(hooks_path, "r") as f:
|
|
522
593
|
existing = f.read()
|
|
523
|
-
if "register_template_hook"
|
|
524
|
-
|
|
594
|
+
if re.search(r"^register_template_hook\(", existing, re.MULTILINE):
|
|
595
|
+
click.echo(
|
|
596
|
+
click.style(" kept: ", dim=True)
|
|
597
|
+
+ "hooks.py (already registers hooks)"
|
|
598
|
+
)
|
|
599
|
+
return False
|
|
525
600
|
|
|
526
601
|
# Warn before clobbering developer-authored hooks.py
|
|
527
602
|
if not _confirm_overwrite(hooks_path, "hooks.py"):
|
|
528
603
|
click.echo(click.style(" skipped: ", dim=True) + "hooks.py (kept)")
|
|
529
|
-
return
|
|
604
|
+
return False
|
|
530
605
|
|
|
531
606
|
os.makedirs(os.path.dirname(hooks_path), exist_ok=True)
|
|
532
607
|
with open(hooks_path, "w") as f:
|
|
533
608
|
f.write(code)
|
|
534
609
|
|
|
610
|
+
# The stubs render hooks/<name>.html. Without those files every page
|
|
611
|
+
# holding one of the slots failed with TemplateNotFound until each stub
|
|
612
|
+
# was rewritten, so an empty template goes with each stub.
|
|
613
|
+
tpl_dir = os.path.join(os.path.dirname(hooks_path), "templates", "hooks")
|
|
614
|
+
os.makedirs(tpl_dir, exist_ok=True)
|
|
615
|
+
for hook in fill_hooks:
|
|
616
|
+
tpl = os.path.join(tpl_dir, f"{hook.replace('.', '_')}.html")
|
|
617
|
+
if not os.path.exists(tpl):
|
|
618
|
+
with open(tpl, "w", encoding="utf-8") as f:
|
|
619
|
+
f.write(f"{{# Fills the {hook} slot. #}}\n")
|
|
620
|
+
return True
|
|
621
|
+
|
|
535
622
|
|
|
536
623
|
def _clean_scaffold_for_refinement(feature_path: str, ns: str, feature_name: str):
|
|
537
624
|
"""Clean up scaffold files that cause false positives in refinement features.
|
|
@@ -906,31 +993,36 @@ def feature_refinement(refiner_name):
|
|
|
906
993
|
ns = d
|
|
907
994
|
break
|
|
908
995
|
|
|
996
|
+
# Each helper says itself why it left a file alone; "scaffolded" is only
|
|
997
|
+
# claimed for what was actually written.
|
|
909
998
|
for model, mixin in extend_models:
|
|
910
|
-
_scaffold_mixin(refiner_path, ns, refiner_name, model, mixin)
|
|
911
|
-
|
|
999
|
+
if _scaffold_mixin(refiner_path, ns, refiner_name, model, mixin):
|
|
1000
|
+
click.echo(
|
|
1001
|
+
click.style(" scaffolded: ", dim=True) + f"models.py <- {mixin}"
|
|
1002
|
+
)
|
|
912
1003
|
|
|
913
1004
|
for target, replacement in override_services:
|
|
914
|
-
_scaffold_service(refiner_path, ns, refiner_name, target, replacement)
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1005
|
+
if _scaffold_service(refiner_path, ns, refiner_name, target, replacement):
|
|
1006
|
+
click.echo(
|
|
1007
|
+
click.style(" scaffolded: ", dim=True)
|
|
1008
|
+
+ f"services.py <- {replacement}"
|
|
1009
|
+
)
|
|
918
1010
|
|
|
919
|
-
_scaffold_init(
|
|
1011
|
+
if _scaffold_init(
|
|
920
1012
|
refiner_path,
|
|
921
1013
|
ns,
|
|
922
1014
|
refiner_name,
|
|
923
1015
|
base_short,
|
|
924
1016
|
extend_models,
|
|
925
1017
|
override_services,
|
|
926
|
-
)
|
|
927
|
-
|
|
1018
|
+
):
|
|
1019
|
+
click.echo(click.style(" scaffolded: ", dim=True) + "__init__.py")
|
|
928
1020
|
|
|
929
|
-
if fill_hooks:
|
|
930
|
-
_scaffold_hooks(refiner_path, ns, refiner_name, fill_hooks)
|
|
1021
|
+
if fill_hooks and _scaffold_hooks(refiner_path, ns, refiner_name, fill_hooks):
|
|
931
1022
|
click.echo(
|
|
932
1023
|
click.style(" scaffolded: ", dim=True)
|
|
933
|
-
+ f"hooks.py <- {len(fill_hooks)} hook slot(s)"
|
|
1024
|
+
+ f"hooks.py <- {len(fill_hooks)} hook slot(s), with empty "
|
|
1025
|
+
"templates/hooks/*.html"
|
|
934
1026
|
)
|
|
935
1027
|
|
|
936
1028
|
# Clean up scaffold files that are not needed for refinement features
|