splent-cli 1.24.0__tar.gz → 1.25.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.24.0/src/splent_cli.egg-info → splent_cli-1.25.0}/PKG-INFO +2 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/pyproject.toml +2 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_features.py +11 -3
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_github.py +28 -14
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_releases.py +16 -12
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_migrate.py +18 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_reset.py +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_rollback.py +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_seed.py +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/doctor.py +4 -4
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/env/env_set.py +20 -9
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_attach.py +14 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_contract.py +3 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_create.py +4 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_detach.py +3 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_fork.py +3 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_pip_install.py +14 -4
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_refine.py +10 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_release.py +187 -20
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_remove.py +4 -3
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_rename.py +5 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_test.py +12 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_build.py +16 -8
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_create.py +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_deploy.py +13 -10
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_drift.py +8 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_env.py +31 -5
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_release.py +8 -8
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_run.py +59 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_sync_template.py +9 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/release/release_core.py +13 -11
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/release/release_resume.py +13 -12
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_add_constraints.py +2 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_add_feature.py +6 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_remove_feature.py +2 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_utils.py +34 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/tokens_setup.py +13 -8
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/context.py +35 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/preflight.py +6 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/release.py +151 -41
- splent_cli-1.25.0/src/splent_cli/services/release_dry_run.py +563 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/release_gate.py +42 -26
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/spl_store.py +53 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/contract_freshness.py +27 -4
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/dynamic_imports.py +50 -5
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/feature_utils.py +63 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/forge.py +14 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/io_utils.py +86 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/template_drift.py +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0/src/splent_cli.egg-info}/PKG-INFO +2 -2
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli.egg-info/SOURCES.txt +1 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli.egg-info/requires.txt +1 -1
- {splent_cli-1.24.0 → splent_cli-1.25.0}/LICENSE +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/README.md +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/setup.cfg +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_migrate_catalog.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/pypi_quota.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/services/registry.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.24.0 → splent_cli-1.25.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.25.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
|
|
@@ -14,7 +14,7 @@ Requires-Dist: packaging>=24.0
|
|
|
14
14
|
Requires-Dist: python-dotenv>=1.0
|
|
15
15
|
Requires-Dist: pyyaml>=6.0
|
|
16
16
|
Requires-Dist: requests>=2.31.0
|
|
17
|
-
Requires-Dist: splent_framework<2,>=1.15.
|
|
17
|
+
Requires-Dist: splent_framework<2,>=1.15.1
|
|
18
18
|
Requires-Dist: tomli_w>=1.0.0
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: setuptools==80.3.1; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "splent_cli"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.25.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"
|
|
@@ -24,7 +24,7 @@ dependencies = [
|
|
|
24
24
|
"requests>=2.31.0",
|
|
25
25
|
# The CLI drives the framework (paths, feature order, migrations, the
|
|
26
26
|
# app factory) and only works with the same major version of it.
|
|
27
|
-
"splent_framework>=1.15.
|
|
27
|
+
"splent_framework>=1.15.1,<2",
|
|
28
28
|
"tomli_w>=1.0.0",
|
|
29
29
|
]
|
|
30
30
|
|
|
@@ -11,6 +11,8 @@ import tomllib
|
|
|
11
11
|
|
|
12
12
|
from splent_cli.services import context
|
|
13
13
|
from splent_cli.utils.feature_utils import normalize_namespace, read_features_from_data
|
|
14
|
+
from splent_cli.utils.lifecycle import state_rank
|
|
15
|
+
from splent_cli.utils.manifest import feature_key, get_feature_state
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def _pkg_installed(name: str) -> bool:
|
|
@@ -125,11 +127,17 @@ def check_features():
|
|
|
125
127
|
else:
|
|
126
128
|
_fail("No symlink in product/features/")
|
|
127
129
|
|
|
128
|
-
# 3. Pip install
|
|
129
|
-
|
|
130
|
+
# 3. Pip install. Features are installed in the product's web
|
|
131
|
+
# container, not in the CLI's, so the manifest the install script
|
|
132
|
+
# updates is what says so. Checking this interpreter alone flagged
|
|
133
|
+
# every feature of a running product as not installed.
|
|
134
|
+
state = get_feature_state(product_path, feature_key(org_safe, name, version))
|
|
135
|
+
if state_rank(state) >= state_rank("installed"):
|
|
136
|
+
_ok(f"Installed in the product container ({state})")
|
|
137
|
+
elif _pkg_installed(name):
|
|
130
138
|
_ok("pip installed")
|
|
131
139
|
else:
|
|
132
|
-
_warn("Not
|
|
140
|
+
_warn("Not installed yet (product:derive installs it)")
|
|
133
141
|
|
|
134
142
|
# 4. Git state (editable only)
|
|
135
143
|
if not version:
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import urllib.request
|
|
3
2
|
import urllib.error
|
|
4
3
|
import json
|
|
@@ -7,32 +6,29 @@ import click
|
|
|
7
6
|
from splent_cli.utils import forge
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
9
|
+
def _check_forge():
|
|
10
|
+
# The forge checked is whatever the workspace is configured against:
|
|
11
|
+
# git.splent.io by default, another Forgejo, Gitea or github.com when
|
|
12
|
+
# SPLENT_FORGE_HOST says so.
|
|
18
13
|
forge_host = forge.host()
|
|
19
14
|
token_var = forge.token_env_name()
|
|
15
|
+
user_var = forge.user_env_name()
|
|
20
16
|
|
|
21
17
|
click.echo(click.style(f"\n{forge_host} Credentials Check\n", fg="cyan", bold=True))
|
|
22
18
|
|
|
23
|
-
user =
|
|
19
|
+
user = (forge.user() or "").strip()
|
|
24
20
|
# Resolved through forge so a self-hosted forge reads SPLENT_FORGE_TOKEN
|
|
25
21
|
# while GitHub keeps reading GITHUB_TOKEN, as it always has.
|
|
26
22
|
token = (forge.token() or "").strip()
|
|
27
23
|
|
|
28
24
|
# --- presence checks ---
|
|
29
25
|
if not user:
|
|
30
|
-
click.echo(click.style("[✖] ", fg="red") + "
|
|
26
|
+
click.echo(click.style("[✖] ", fg="red") + f"{user_var} not set in .env")
|
|
31
27
|
click.secho(
|
|
32
28
|
" Run 'splent tokens:setup' for setup instructions.", fg="bright_black"
|
|
33
29
|
)
|
|
34
30
|
raise SystemExit(1)
|
|
35
|
-
click.echo(click.style("[✔] ", fg="green") + f"
|
|
31
|
+
click.echo(click.style("[✔] ", fg="green") + f"{user_var} = {user}")
|
|
36
32
|
|
|
37
33
|
if not token:
|
|
38
34
|
click.echo(click.style("[✖] ", fg="red") + f"{token_var} not set in .env")
|
|
@@ -98,7 +94,7 @@ def check_github():
|
|
|
98
94
|
if api_login.lower() != user.lower():
|
|
99
95
|
click.echo(
|
|
100
96
|
click.style("[⚠] ", fg="yellow")
|
|
101
|
-
+ f"Token belongs to '{api_login}', but
|
|
97
|
+
+ f"Token belongs to '{api_login}', but {user_var} is '{user}'"
|
|
102
98
|
)
|
|
103
99
|
else:
|
|
104
100
|
click.echo(click.style("[✔] ", fg="green") + f"Authenticated as {api_login}")
|
|
@@ -123,4 +119,22 @@ def check_github():
|
|
|
123
119
|
click.secho(f"Credentials for {forge_host} OK.", fg="green")
|
|
124
120
|
|
|
125
121
|
|
|
126
|
-
|
|
122
|
+
@click.command(
|
|
123
|
+
"check:forge",
|
|
124
|
+
short_help="Verify the git forge credentials (SPLENT_FORGE_USER / token).",
|
|
125
|
+
)
|
|
126
|
+
def check_forge():
|
|
127
|
+
"""Check that the forge user and token in .env work against the forge's
|
|
128
|
+
API (git.splent.io unless SPLENT_FORGE_HOST says otherwise)."""
|
|
129
|
+
_check_forge()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# The name it had while the features lived on github.com. Kept so scripts and
|
|
133
|
+
# older docs keep working, but out of the help listing.
|
|
134
|
+
@click.command("check:github", hidden=True)
|
|
135
|
+
def check_github():
|
|
136
|
+
"""Alias of check:forge."""
|
|
137
|
+
_check_forge()
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
cli_commands = [check_forge, check_github]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
check:releases — Report
|
|
2
|
+
check:releases — Report forge/PyPI divergence across the workspace.
|
|
3
3
|
|
|
4
4
|
Answers, without anyone having to read a published index by hand, the two
|
|
5
5
|
questions a two-channel release can go wrong in:
|
|
@@ -28,6 +28,7 @@ import click
|
|
|
28
28
|
|
|
29
29
|
from splent_cli.services import context, registry, release_gate
|
|
30
30
|
from splent_cli.services.release import extract_repo, read_project_name
|
|
31
|
+
from splent_cli.utils import forge
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
CORE_PACKAGES = ("splent_cli", "splent_framework")
|
|
@@ -142,7 +143,7 @@ def discover_packages(
|
|
|
142
143
|
|
|
143
144
|
@click.command(
|
|
144
145
|
"check:releases",
|
|
145
|
-
short_help="Report
|
|
146
|
+
short_help="Report forge/PyPI divergence for every package in the workspace.",
|
|
146
147
|
)
|
|
147
148
|
@click.option("--feature", "only", default=None, help="Check one package by name.")
|
|
148
149
|
@click.option(
|
|
@@ -168,7 +169,7 @@ def discover_packages(
|
|
|
168
169
|
def check_releases(only, include_core, include_products, divergent_only):
|
|
169
170
|
"""Compare git tags against PyPI releases for the whole workspace."""
|
|
170
171
|
workspace = str(context.workspace())
|
|
171
|
-
token =
|
|
172
|
+
token = forge.token()
|
|
172
173
|
|
|
173
174
|
packages = discover_packages(
|
|
174
175
|
workspace,
|
|
@@ -224,12 +225,12 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
224
225
|
|
|
225
226
|
org, repo_name = repo.split("/", 1)
|
|
226
227
|
wants_pypi = release_gate.PYPI in channels
|
|
227
|
-
wants_github = release_gate.
|
|
228
|
+
wants_github = release_gate.FORGE in channels
|
|
228
229
|
|
|
229
230
|
try:
|
|
230
231
|
tags = registry.list_semver_tags(org, repo_name, token, quiet=False)
|
|
231
232
|
except registry.RegistryError as e:
|
|
232
|
-
reason = _reason(e, "
|
|
233
|
+
reason = _reason(e, "the forge")
|
|
233
234
|
unanswered.append((name, reason))
|
|
234
235
|
click.echo(click.style(" [!] ", fg="yellow") + f"{name}: {reason}")
|
|
235
236
|
continue
|
|
@@ -249,7 +250,7 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
249
250
|
try:
|
|
250
251
|
release_tags = registry.list_release_tags(org, repo_name, token)
|
|
251
252
|
except registry.RegistryError as e:
|
|
252
|
-
reason = _reason(e, "
|
|
253
|
+
reason = _reason(e, "the forge") + " when listing releases"
|
|
253
254
|
unanswered.append((name, reason))
|
|
254
255
|
click.echo(click.style(" [!] ", fg="yellow") + f"{name}: {reason}")
|
|
255
256
|
continue
|
|
@@ -277,7 +278,7 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
277
278
|
if missing:
|
|
278
279
|
tag_without_release.append((name, missing))
|
|
279
280
|
problems.append(
|
|
280
|
-
f"{len(missing)} tag(s) with no
|
|
281
|
+
f"{len(missing)} tag(s) with no release on the forge: "
|
|
281
282
|
+ ", ".join(missing[:6])
|
|
282
283
|
+ (" ..." if len(missing) > 6 else "")
|
|
283
284
|
)
|
|
@@ -290,7 +291,7 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
290
291
|
if not wants_pypi:
|
|
291
292
|
click.echo(
|
|
292
293
|
click.style(" [OK] ", fg="green")
|
|
293
|
-
+ f"{name}:
|
|
294
|
+
+ f"{name}: forge only by declaration, {len(tags)} tag(s)"
|
|
294
295
|
)
|
|
295
296
|
elif not tags and not pypi_versions:
|
|
296
297
|
click.echo(
|
|
@@ -310,7 +311,8 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
310
311
|
if not total and not unanswered:
|
|
311
312
|
checked = len(packages) - len(no_remote)
|
|
312
313
|
click.secho(
|
|
313
|
-
f"
|
|
314
|
+
f" The forge and PyPI agree for all {checked} package(s) checked.",
|
|
315
|
+
fg="green",
|
|
314
316
|
)
|
|
315
317
|
if no_remote:
|
|
316
318
|
click.secho(
|
|
@@ -320,7 +322,8 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
320
322
|
click.echo()
|
|
321
323
|
if not token:
|
|
322
324
|
click.secho(
|
|
323
|
-
" Set
|
|
325
|
+
f" Set {forge.token_env_name()} to avoid rate limits on this command.",
|
|
326
|
+
fg="yellow",
|
|
324
327
|
)
|
|
325
328
|
click.echo()
|
|
326
329
|
return
|
|
@@ -358,7 +361,7 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
358
361
|
|
|
359
362
|
if tag_without_release:
|
|
360
363
|
click.secho(
|
|
361
|
-
f" {len(tag_without_release)} package(s) tagged with no
|
|
364
|
+
f" {len(tag_without_release)} package(s) tagged with no release on the forge",
|
|
362
365
|
fg="red",
|
|
363
366
|
bold=True,
|
|
364
367
|
)
|
|
@@ -384,7 +387,8 @@ def check_releases(only, include_core, include_products, divergent_only):
|
|
|
384
387
|
|
|
385
388
|
if not token:
|
|
386
389
|
click.secho(
|
|
387
|
-
" Set
|
|
390
|
+
f" Set {forge.token_env_name()} to avoid rate limits on this command.",
|
|
391
|
+
fg="yellow",
|
|
388
392
|
)
|
|
389
393
|
click.echo()
|
|
390
394
|
|
|
@@ -61,6 +61,22 @@ def _format_migration(path: str) -> None:
|
|
|
61
61
|
subprocess.run([ruff, "format", "--quiet", path], check=False)
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def _hand_to_directory_owner(path: str) -> None:
|
|
65
|
+
"""Give a generated migration the owner of the directory it lands in.
|
|
66
|
+
|
|
67
|
+
The CLI runs as root inside its container, so Alembic wrote the file as
|
|
68
|
+
root:root 0644. On the host that is a file the user cannot save, and a
|
|
69
|
+
blank ``--empty`` migration exists precisely to be edited by hand.
|
|
70
|
+
"""
|
|
71
|
+
if not (hasattr(os, "geteuid") and os.geteuid() == 0):
|
|
72
|
+
return
|
|
73
|
+
try:
|
|
74
|
+
st = os.stat(os.path.dirname(path))
|
|
75
|
+
os.chown(path, st.st_uid, st.st_gid)
|
|
76
|
+
except OSError:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
64
80
|
@requires_db
|
|
65
81
|
@click.command(
|
|
66
82
|
"db:migrate",
|
|
@@ -190,6 +206,7 @@ def db_migrate(feature, message, empty):
|
|
|
190
206
|
if empty:
|
|
191
207
|
if newest:
|
|
192
208
|
_format_migration(newest)
|
|
209
|
+
_hand_to_directory_owner(newest)
|
|
193
210
|
click.echo(
|
|
194
211
|
click.style(
|
|
195
212
|
f" 📝 {feat}: blank migration generated — "
|
|
@@ -208,6 +225,7 @@ def db_migrate(feature, message, empty):
|
|
|
208
225
|
click.echo(click.style(f" ✔ {feat}: up to date", fg="green"))
|
|
209
226
|
else:
|
|
210
227
|
_format_migration(newest)
|
|
228
|
+
_hand_to_directory_owner(newest)
|
|
211
229
|
click.echo(
|
|
212
230
|
click.style(f" 📝 {feat}: new migration generated", fg="cyan")
|
|
213
231
|
)
|
|
@@ -114,6 +114,7 @@ def _entry_lookup() -> dict:
|
|
|
114
114
|
@click.argument("feature_name", required=False)
|
|
115
115
|
@click.option("-y", "--yes", is_flag=True, help="Confirm without prompting.")
|
|
116
116
|
@context.requires_product
|
|
117
|
+
@context.refuses_production
|
|
117
118
|
def db_reset(feature_name, yes):
|
|
118
119
|
"""Reset the database.
|
|
119
120
|
|
|
@@ -69,6 +69,7 @@ def _find_dependents(feature: str, product_dir: str) -> list[str]:
|
|
|
69
69
|
)
|
|
70
70
|
@click.option("--cascade", is_flag=True, help="Also rollback dependent features.")
|
|
71
71
|
@context.requires_product
|
|
72
|
+
@context.refuses_production
|
|
72
73
|
def db_rollback(feature, steps, cascade):
|
|
73
74
|
app = current_app
|
|
74
75
|
product_path = PathUtils.get_app_base_dir()
|
|
@@ -177,6 +177,7 @@ def _truncate_data():
|
|
|
177
177
|
@click.option("-y", "--yes", is_flag=True, help="Skip confirmation prompts.")
|
|
178
178
|
@click.argument("module", required=False)
|
|
179
179
|
@context.requires_product
|
|
180
|
+
@context.refuses_production
|
|
180
181
|
def db_seed(reset, yes, module):
|
|
181
182
|
if reset:
|
|
182
183
|
if yes or click.confirm(
|
|
@@ -12,7 +12,7 @@ from splent_cli.commands.check.check_env import check_env
|
|
|
12
12
|
from splent_cli.commands.check.check_pyproject import check_pyproject
|
|
13
13
|
from splent_cli.commands.check.check_features import check_features
|
|
14
14
|
from splent_cli.commands.check.check_docker import check_docker
|
|
15
|
-
from splent_cli.commands.check.check_github import
|
|
15
|
+
from splent_cli.commands.check.check_github import check_forge
|
|
16
16
|
from splent_cli.commands.check.check_pypi import check_pypi
|
|
17
17
|
from splent_cli.commands.check.check_infra import check_infra
|
|
18
18
|
from splent_cli.commands.check.check_product import check_product
|
|
@@ -49,7 +49,7 @@ SECTIONS = [
|
|
|
49
49
|
(
|
|
50
50
|
"Credentials",
|
|
51
51
|
[
|
|
52
|
-
("check:
|
|
52
|
+
("check:forge", check_forge, False, True),
|
|
53
53
|
("check:pypi", check_pypi, False, True),
|
|
54
54
|
],
|
|
55
55
|
),
|
|
@@ -110,10 +110,10 @@ def doctor(fast):
|
|
|
110
110
|
Environment — Python, env vars, pyproject, Docker, infrastructure
|
|
111
111
|
Product — product health, feature cache/symlinks, feature status
|
|
112
112
|
Services — containers, database migrations
|
|
113
|
-
Credentials —
|
|
113
|
+
Credentials — git forge and PyPI tokens (skipped with --fast)
|
|
114
114
|
|
|
115
115
|
\b
|
|
116
|
-
Use --fast to skip network checks (
|
|
116
|
+
Use --fast to skip network checks (git forge, PyPI) and database checks.
|
|
117
117
|
|
|
118
118
|
\b
|
|
119
119
|
For full product validation (UVL + contracts + imports), run:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import click
|
|
2
2
|
from splent_cli.services import context
|
|
3
|
+
from splent_cli.utils import forge
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
# -------------------------
|
|
@@ -67,16 +68,19 @@ def set_mode_interactive():
|
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def set_github_interactive():
|
|
70
|
-
user
|
|
71
|
-
|
|
71
|
+
"""Ask for the forge user and token and store them under the variables
|
|
72
|
+
this forge reads (SPLENT_FORGE_* on git.splent.io, GITHUB_* on github.com)."""
|
|
73
|
+
host = forge.host()
|
|
74
|
+
user = click.prompt(f"{host} username", type=str)
|
|
75
|
+
token = click.prompt(f"{host} access token", hide_input=True)
|
|
72
76
|
|
|
73
|
-
user = _validate_env_value(user, "
|
|
74
|
-
token = _validate_env_value(token, "
|
|
77
|
+
user = _validate_env_value(user, f"{host} username")
|
|
78
|
+
token = _validate_env_value(token, f"{host} token")
|
|
75
79
|
|
|
76
|
-
set_var(
|
|
77
|
-
set_var(
|
|
80
|
+
set_var(forge.user_env_name(), user)
|
|
81
|
+
set_var(forge.token_env_name(), token)
|
|
78
82
|
|
|
79
|
-
click.secho("✔
|
|
83
|
+
click.secho(f"✔ {host} configuration updated.", fg="green")
|
|
80
84
|
remind_source()
|
|
81
85
|
|
|
82
86
|
|
|
@@ -124,8 +128,15 @@ def env_set_mode():
|
|
|
124
128
|
set_mode_interactive()
|
|
125
129
|
|
|
126
130
|
|
|
127
|
-
@env_set_group.command("
|
|
131
|
+
@env_set_group.command("forge")
|
|
132
|
+
def env_set_forge():
|
|
133
|
+
"""Set the git forge user and token."""
|
|
134
|
+
set_github_interactive()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@env_set_group.command("github", hidden=True)
|
|
128
138
|
def env_set_github():
|
|
139
|
+
"""Alias of `env:set forge`."""
|
|
129
140
|
set_github_interactive()
|
|
130
141
|
|
|
131
142
|
|
|
@@ -143,7 +154,7 @@ def run_wizard():
|
|
|
143
154
|
while True:
|
|
144
155
|
click.echo("\n=== SPLENT ENV CONFIG WIZARD ===")
|
|
145
156
|
click.echo("1. Set mode (dev/prod)")
|
|
146
|
-
click.echo("2. Configure
|
|
157
|
+
click.echo("2. Configure git forge credentials")
|
|
147
158
|
click.echo("3. Configure PyPI token")
|
|
148
159
|
click.echo("4. Exit")
|
|
149
160
|
click.echo("--------------------------------")
|
|
@@ -10,7 +10,9 @@ from splent_cli.utils.feature_utils import (
|
|
|
10
10
|
product_namespace_spelling,
|
|
11
11
|
prune_feature_links,
|
|
12
12
|
read_feature_list,
|
|
13
|
+
read_project_name,
|
|
13
14
|
remove_feature_link,
|
|
15
|
+
set_feature_distribution,
|
|
14
16
|
write_features_to_data,
|
|
15
17
|
)
|
|
16
18
|
from splent_cli.utils.io_utils import load_toml, atomic_write
|
|
@@ -147,6 +149,18 @@ def feature_attach(feature_identifier, version, env_scope):
|
|
|
147
149
|
scope_label = f" ({env_scope} only)" if env_scope else ""
|
|
148
150
|
click.echo(f" {short}@{version} attached{scope_label}")
|
|
149
151
|
|
|
152
|
+
# ── PyPI name, when it is not the feature's own ──────────────────
|
|
153
|
+
# The production image installs pinned features from PyPI, so it has to
|
|
154
|
+
# know the name this version was published under. A release under a name
|
|
155
|
+
# of one's own (splent-feature-notes-alice) keeps the import package, and
|
|
156
|
+
# this pin, as splent_feature_notes.
|
|
157
|
+
distribution = read_project_name(versioned_dir)
|
|
158
|
+
data = load_toml(pyproject_path, what="pyproject.toml")
|
|
159
|
+
if set_feature_distribution(data, feature_name, distribution):
|
|
160
|
+
atomic_write(pyproject_path, tomli_w.dumps(data))
|
|
161
|
+
if distribution and distribution != feature_name:
|
|
162
|
+
click.echo(f" pypi {short} is published on PyPI as {distribution}")
|
|
163
|
+
|
|
150
164
|
# ── Create/update symlink ─────────────────────────────────────────
|
|
151
165
|
product_features_dir = os.path.join(product_path, "features", namespace_fs)
|
|
152
166
|
os.makedirs(product_features_dir, exist_ok=True)
|
|
@@ -22,6 +22,7 @@ from pathlib import Path
|
|
|
22
22
|
from splent_cli.services import context
|
|
23
23
|
from splent_cli.commands.feature.feature_release import infer_contract, write_contract
|
|
24
24
|
from splent_cli.utils.feature_utils import normalize_namespace
|
|
25
|
+
from splent_cli.utils.contract_freshness import mark_contract_fresh
|
|
25
26
|
from splent_cli.utils.io_utils import load_toml
|
|
26
27
|
|
|
27
28
|
|
|
@@ -351,6 +352,8 @@ def feature_contract(feature_ref, write):
|
|
|
351
352
|
|
|
352
353
|
if not has_changes:
|
|
353
354
|
click.secho(" ✅ Contract is already up to date.", fg="green")
|
|
355
|
+
if write:
|
|
356
|
+
mark_contract_fresh(str(cache_path))
|
|
354
357
|
# Reported here too: a settings gap has nothing to do with whether
|
|
355
358
|
# the contract changed, and an up to date contract is exactly when
|
|
356
359
|
# nobody would think to look.
|
|
@@ -58,6 +58,10 @@ def _build_file_map(feature_type, short_name, docker=False):
|
|
|
58
58
|
base_common = {
|
|
59
59
|
".gitignore": f"{T}feature_.gitignore.j2",
|
|
60
60
|
"MANIFEST.in": f"{T}feature_MANIFEST.in.j2",
|
|
61
|
+
# pyproject.toml names it as the readme, and the package build reads
|
|
62
|
+
# it as the long description PyPI shows. Without the file the build
|
|
63
|
+
# warned and the published page was blank.
|
|
64
|
+
"README.md": f"{T}feature_README.md.j2",
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
if feature_type == "full":
|
|
@@ -9,6 +9,7 @@ from splent_cli.utils.feature_utils import (
|
|
|
9
9
|
parse_feature_entry,
|
|
10
10
|
read_feature_list,
|
|
11
11
|
remove_feature_link,
|
|
12
|
+
set_feature_distribution,
|
|
12
13
|
write_features_to_data,
|
|
13
14
|
)
|
|
14
15
|
from splent_cli.utils.io_utils import load_toml, atomic_write
|
|
@@ -108,6 +109,8 @@ def feature_detach(feature_identifier, version, force):
|
|
|
108
109
|
changed = True
|
|
109
110
|
|
|
110
111
|
if changed:
|
|
112
|
+
# Back to the editable checkout, which is never installed from PyPI.
|
|
113
|
+
set_feature_distribution(data, feature_name, None)
|
|
111
114
|
atomic_write(pyproject_path, tomli_w.dumps(data))
|
|
112
115
|
|
|
113
116
|
click.echo(f" {short}@{version} removed from pyproject.toml")
|
|
@@ -24,11 +24,12 @@ def _forge_label() -> str:
|
|
|
24
24
|
)
|
|
25
25
|
def feature_fork(feature_name, version):
|
|
26
26
|
token = forge.token()
|
|
27
|
-
github_user =
|
|
27
|
+
github_user = forge.user()
|
|
28
28
|
|
|
29
29
|
if not token or not github_user:
|
|
30
30
|
click.secho(
|
|
31
|
-
f"✖ Error: {forge.token_env_name()} and
|
|
31
|
+
f"✖ Error: {forge.token_env_name()} and {forge.user_env_name()} must be set",
|
|
32
|
+
fg="red",
|
|
32
33
|
)
|
|
33
34
|
raise SystemExit(1)
|
|
34
35
|
|
{splent_cli-1.24.0 → splent_cli-1.25.0}/src/splent_cli/commands/feature/feature_pip_install.py
RENAMED
|
@@ -28,6 +28,10 @@ Three things about this are worth stating plainly rather than discovering:
|
|
|
28
28
|
* the summary says which features came from which channel. An image built
|
|
29
29
|
from git tags is reproducible, but you should know that is what you have.
|
|
30
30
|
|
|
31
|
+
A feature published on PyPI under a name of its own (its [project].name is
|
|
32
|
+
not its directory name) is listed in [tool.splent.distributions], which
|
|
33
|
+
feature:attach writes, and is installed by that name.
|
|
34
|
+
|
|
31
35
|
Example:
|
|
32
36
|
splent-io/splent_feature_auth@v1.2.7 -> pip install splent_feature_auth==1.2.7
|
|
33
37
|
-> or git+https://github.com/…@v1.2.7
|
|
@@ -41,6 +45,7 @@ import click
|
|
|
41
45
|
import tomllib
|
|
42
46
|
|
|
43
47
|
from splent_cli.services import context
|
|
48
|
+
from splent_cli.utils.feature_utils import feature_distribution
|
|
44
49
|
from splent_cli.utils.git_url import https_url, namespace_spellings
|
|
45
50
|
from splent_cli.utils.proc import run
|
|
46
51
|
|
|
@@ -100,7 +105,9 @@ def _pypi_does_not_have_it(output: str) -> bool:
|
|
|
100
105
|
)
|
|
101
106
|
|
|
102
107
|
|
|
103
|
-
def _git_candidates(
|
|
108
|
+
def _git_candidates(
|
|
109
|
+
namespace: str, name: str, ref: str, dist: str | None = None
|
|
110
|
+
) -> list[tuple[str, str]]:
|
|
104
111
|
"""Ordered (real, display) pip requirements for the tag, one per spelling.
|
|
105
112
|
|
|
106
113
|
The entry carries the Python namespace (``splent_io``) while the repo
|
|
@@ -110,7 +117,9 @@ def _git_candidates(namespace: str, name: str, ref: str) -> list[tuple[str, str]
|
|
|
110
117
|
candidates = []
|
|
111
118
|
for spelling in namespace_spellings(namespace):
|
|
112
119
|
real, display = https_url(spelling, name)
|
|
113
|
-
candidates.append(
|
|
120
|
+
candidates.append(
|
|
121
|
+
(f"{dist or name} @ git+{real}@{ref}", f"git+{display}@{ref}")
|
|
122
|
+
)
|
|
114
123
|
return candidates
|
|
115
124
|
|
|
116
125
|
|
|
@@ -190,7 +199,8 @@ def feature_pip_install(pypi_only):
|
|
|
190
199
|
from_git = []
|
|
191
200
|
for entry in features:
|
|
192
201
|
namespace, name, version, ref = _parse_feature_entry(entry)
|
|
193
|
-
|
|
202
|
+
dist = feature_distribution(data, name)
|
|
203
|
+
spec = f"{dist}=={version}" if version else dist
|
|
194
204
|
if not version:
|
|
195
205
|
unpinned.append(name)
|
|
196
206
|
|
|
@@ -216,7 +226,7 @@ def feature_pip_install(pypi_only):
|
|
|
216
226
|
fg="yellow",
|
|
217
227
|
)
|
|
218
228
|
git_output = ""
|
|
219
|
-
for real_spec, display_spec in _git_candidates(namespace, name, ref):
|
|
229
|
+
for real_spec, display_spec in _git_candidates(namespace, name, ref, dist):
|
|
220
230
|
ok, git_output = _pip_install(real_spec)
|
|
221
231
|
if ok:
|
|
222
232
|
click.echo(f" ok {display_spec}")
|
|
@@ -14,7 +14,12 @@ import click
|
|
|
14
14
|
|
|
15
15
|
from splent_cli.services import context
|
|
16
16
|
from splent_cli.utils.feature_utils import normalize_namespace, read_features_from_data
|
|
17
|
-
from splent_cli.utils.io_utils import
|
|
17
|
+
from splent_cli.utils.io_utils import (
|
|
18
|
+
atomic_write,
|
|
19
|
+
backup_file,
|
|
20
|
+
give_tree_to_its_owner,
|
|
21
|
+
load_toml,
|
|
22
|
+
)
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
DEFAULT_NAMESPACE = os.getenv("SPLENT_DEFAULT_NAMESPACE", "splent_io")
|
|
@@ -1040,6 +1045,10 @@ def feature_refinement(refiner_name):
|
|
|
1040
1045
|
except Exception:
|
|
1041
1046
|
pass
|
|
1042
1047
|
|
|
1048
|
+
# Everything above ran as root in the CLI container. templates/hooks/ was
|
|
1049
|
+
# left root's, and Tutorial 7 asks the reader to add files to it.
|
|
1050
|
+
give_tree_to_its_owner(refiner_path)
|
|
1051
|
+
|
|
1043
1052
|
# ── Register the refinement in the active product ───────────────
|
|
1044
1053
|
# A refinement only takes effect once the feature is part of the product
|
|
1045
1054
|
# (migrations, composition, feature:xray). Add it now so the user does not
|