splent-cli 1.29.0__tar.gz → 1.29.1__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.29.0/src/splent_cli.egg-info → splent_cli-1.29.1}/PKG-INFO +1 -1
- {splent_cli-1.29.0 → splent_cli-1.29.1}/pyproject.toml +1 -1
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_release.py +96 -1
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_release.py +70 -41
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace.py +69 -1
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release.py +54 -4
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/toml_edit.py +48 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.29.0 → splent_cli-1.29.1}/LICENSE +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/README.md +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/setup.cfg +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_test.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_build.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_deploy.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/release/release_resume.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/tokens_setup.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/image_registry.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/preflight.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/pypi_quota.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/registry.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release_dry_run.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/release_gate.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/services/spl_store.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/db_client.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/forge.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/redact.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/refinement_facts.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli/utils/upgrade_plan.py +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/SOURCES.txt +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.29.0 → splent_cli-1.29.1}/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.29.
|
|
3
|
+
Version: 1.29.1
|
|
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
|
|
@@ -3,6 +3,7 @@ feature:release — Release a feature: bump version, tag, publish to the git for
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import ast
|
|
6
|
+
import json
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
8
9
|
import shutil
|
|
@@ -363,6 +364,83 @@ def _extract_commands(commands_path: Path) -> list[str]:
|
|
|
363
364
|
return sorted(set(re.findall(r"""@click\.command\s*\(\s*['"]([^'"]+)['"]""", text)))
|
|
364
365
|
|
|
365
366
|
|
|
367
|
+
def _call_name(node) -> str:
|
|
368
|
+
"""``click.command`` for the decorator call ``@click.command(...)``."""
|
|
369
|
+
func = node.func if isinstance(node, ast.Call) else node
|
|
370
|
+
parts = []
|
|
371
|
+
while isinstance(func, ast.Attribute):
|
|
372
|
+
parts.append(func.attr)
|
|
373
|
+
func = func.value
|
|
374
|
+
if isinstance(func, ast.Name):
|
|
375
|
+
parts.append(func.id)
|
|
376
|
+
return ".".join(reversed(parts))
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _literal(node):
|
|
380
|
+
try:
|
|
381
|
+
return ast.literal_eval(node)
|
|
382
|
+
except (ValueError, SyntaxError, TypeError):
|
|
383
|
+
return None
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _first_line(text) -> str:
|
|
387
|
+
for line in (text or "").strip().splitlines():
|
|
388
|
+
if line.strip():
|
|
389
|
+
return line.strip()
|
|
390
|
+
return ""
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _extract_command_help(commands_path: Path) -> dict:
|
|
394
|
+
"""What each command a feature adds is for, and how it is called.
|
|
395
|
+
|
|
396
|
+
``{name: {"usage": "EMAIL ROLE [OPTIONS]", "help": "..."}}``, read from
|
|
397
|
+
the source without importing it: the arguments in the order they are
|
|
398
|
+
declared, in click's uppercase, ``[OPTIONS]`` when there are any, and the
|
|
399
|
+
first line of ``short_help``, ``help`` or the docstring. The marketplace
|
|
400
|
+
shows it next to each command, which used to be a bare name.
|
|
401
|
+
"""
|
|
402
|
+
if not commands_path.exists():
|
|
403
|
+
return {}
|
|
404
|
+
try:
|
|
405
|
+
tree = ast.parse(commands_path.read_text())
|
|
406
|
+
except SyntaxError:
|
|
407
|
+
return {}
|
|
408
|
+
found = {}
|
|
409
|
+
for node in ast.walk(tree):
|
|
410
|
+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
411
|
+
continue
|
|
412
|
+
name, help_text, usage, has_options = None, "", [], False
|
|
413
|
+
for deco in node.decorator_list:
|
|
414
|
+
if not isinstance(deco, ast.Call):
|
|
415
|
+
continue
|
|
416
|
+
kind = _call_name(deco)
|
|
417
|
+
if kind.endswith("command") and deco.args:
|
|
418
|
+
name = _literal(deco.args[0])
|
|
419
|
+
kwargs = {k.arg: _literal(k.value) for k in deco.keywords}
|
|
420
|
+
help_text = kwargs.get("short_help") or kwargs.get("help") or ""
|
|
421
|
+
elif kind.endswith("argument") and deco.args:
|
|
422
|
+
arg = _literal(deco.args[0])
|
|
423
|
+
if not isinstance(arg, str):
|
|
424
|
+
continue
|
|
425
|
+
kwargs = {k.arg: _literal(k.value) for k in deco.keywords}
|
|
426
|
+
metavar = arg.upper().replace("-", "_")
|
|
427
|
+
if kwargs.get("nargs") == -1:
|
|
428
|
+
metavar += "..."
|
|
429
|
+
optional = kwargs.get("required") is False or "default" in kwargs
|
|
430
|
+
usage.append(f"[{metavar}]" if optional else metavar)
|
|
431
|
+
elif kind.endswith("option"):
|
|
432
|
+
has_options = True
|
|
433
|
+
if not isinstance(name, str):
|
|
434
|
+
continue
|
|
435
|
+
if has_options:
|
|
436
|
+
usage.append("[OPTIONS]")
|
|
437
|
+
found[name] = {
|
|
438
|
+
"usage": " ".join(usage),
|
|
439
|
+
"help": _first_line(help_text) or _first_line(ast.get_docstring(node)),
|
|
440
|
+
}
|
|
441
|
+
return dict(sorted(found.items()))
|
|
442
|
+
|
|
443
|
+
|
|
366
444
|
def _feature_sources(src_dir: Path):
|
|
367
445
|
"""The feature's own Python files, without its tests.
|
|
368
446
|
|
|
@@ -693,6 +771,7 @@ def infer_contract(
|
|
|
693
771
|
templates = _extract_templates(src_dir)
|
|
694
772
|
template_hook_slots = _extract_template_hook_slots(src_dir)
|
|
695
773
|
commands = _extract_commands(src_dir / "commands.py")
|
|
774
|
+
commands_help = _extract_command_help(src_dir / "commands.py")
|
|
696
775
|
docker = _extract_docker(feature_root)
|
|
697
776
|
docker_contract = _extract_docker_contract(feature_root)
|
|
698
777
|
req_features, env_vars = _scan_dependencies(src_dir, feature_name)
|
|
@@ -751,6 +830,7 @@ def infer_contract(
|
|
|
751
830
|
"blueprints": blueprints,
|
|
752
831
|
"models": models,
|
|
753
832
|
"commands": commands,
|
|
833
|
+
"commands_help": commands_help,
|
|
754
834
|
"hooks": hooks,
|
|
755
835
|
"services": services,
|
|
756
836
|
"signals": signals_provided,
|
|
@@ -945,7 +1025,8 @@ def write_contract(pyproject_path: str, contract: dict, feature_name: str) -> No
|
|
|
945
1025
|
f"signals = {_toml_value(contract.get('signals', []))}\n"
|
|
946
1026
|
f"translations = {_toml_value(contract.get('translations', []))}\n"
|
|
947
1027
|
f"docker = {_toml_value(contract['docker'])}\n"
|
|
948
|
-
"
|
|
1028
|
+
+ _commands_help_block(contract.get("commands_help") or {})
|
|
1029
|
+
+ "\n"
|
|
949
1030
|
"[tool.splent.contract.requires]\n"
|
|
950
1031
|
f"features = {_toml_value(contract['requires_features'])}\n"
|
|
951
1032
|
+ (
|
|
@@ -989,6 +1070,20 @@ def write_contract(pyproject_path: str, contract: dict, feature_name: str) -> No
|
|
|
989
1070
|
path.write_text(text + contract_block + refinement_block)
|
|
990
1071
|
|
|
991
1072
|
|
|
1073
|
+
def _commands_help_block(commands_help: dict) -> str:
|
|
1074
|
+
"""The ``[tool.splent.contract.provides.commands_help]`` table, or nothing."""
|
|
1075
|
+
if not commands_help:
|
|
1076
|
+
return ""
|
|
1077
|
+
lines = ["\n[tool.splent.contract.provides.commands_help]\n"]
|
|
1078
|
+
for name, entry in commands_help.items():
|
|
1079
|
+
key = name if re.fullmatch(r"[A-Za-z0-9_-]+", name) else json.dumps(name)
|
|
1080
|
+
lines.append(
|
|
1081
|
+
f"{key} = {{ usage = {json.dumps(entry.get('usage', ''))}, "
|
|
1082
|
+
f"help = {json.dumps(entry.get('help', ''))} }}\n"
|
|
1083
|
+
)
|
|
1084
|
+
return "".join(lines)
|
|
1085
|
+
|
|
1086
|
+
|
|
992
1087
|
# ── Compile assets ────────────────────────────────────────────────────
|
|
993
1088
|
|
|
994
1089
|
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
product:release, a product release: version bump, tag, forge, PyPI and the image.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import contextlib
|
|
6
|
+
import json
|
|
5
7
|
import os
|
|
6
8
|
import subprocess
|
|
9
|
+
import tempfile
|
|
7
10
|
|
|
8
11
|
import click
|
|
9
12
|
from splent_cli.commands.product.product_build import (
|
|
@@ -63,7 +66,32 @@ def release_targets(product: str, version: str, product_path: str, org: str):
|
|
|
63
66
|
return image_registry.image_targets(product, version, org, registries, mirrors)
|
|
64
67
|
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
#: Docker Hub answers to these names in a client's configuration.
|
|
70
|
+
_DOCKER_HUB_HOSTS = ("index.docker.io", "registry-1.docker.io", "docker.io")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@contextlib.contextmanager
|
|
74
|
+
def registry_session(hosts=()):
|
|
75
|
+
"""A docker client configuration of its own for one release's logins and pushes.
|
|
76
|
+
|
|
77
|
+
The host's configuration may hand credentials to a helper, the macOS
|
|
78
|
+
keychain under Docker Desktop, and that helper refuses to store a login
|
|
79
|
+
for a host the keychain already holds a git password for ("The specified
|
|
80
|
+
item already exists in the keychain"), which stopped the marketplace's
|
|
81
|
+
first release. A configuration of its own also keeps the release tokens
|
|
82
|
+
off the machine: it is deleted when the release is done.
|
|
83
|
+
"""
|
|
84
|
+
# An empty credsStore is not enough: the docker client then detects the
|
|
85
|
+
# platform's helper again. An empty helper per host is what makes it keep
|
|
86
|
+
# the login in this file.
|
|
87
|
+
helpers = {host: "" for host in (*hosts, *_DOCKER_HUB_HOSTS) if host}
|
|
88
|
+
with tempfile.TemporaryDirectory(prefix="splent-docker-") as config_dir:
|
|
89
|
+
with open(os.path.join(config_dir, "config.json"), "w") as handle:
|
|
90
|
+
json.dump({"auths": {}, "credHelpers": helpers}, handle)
|
|
91
|
+
yield {**os.environ, "DOCKER_CONFIG": config_dir}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _login(target, env=None) -> bool:
|
|
67
95
|
user, secret = target.credentials()
|
|
68
96
|
if not user or not secret:
|
|
69
97
|
return False
|
|
@@ -71,7 +99,7 @@ def _login(target) -> bool:
|
|
|
71
99
|
if target.host:
|
|
72
100
|
command.insert(2, target.host)
|
|
73
101
|
try:
|
|
74
|
-
run(command, input=secret, capture=True)
|
|
102
|
+
run(command, input=secret, capture=True, env=env)
|
|
75
103
|
except subprocess.CalledProcessError:
|
|
76
104
|
return False
|
|
77
105
|
return True
|
|
@@ -105,51 +133,52 @@ def release_docker_image(product: str, version: str, docker_dir: str, targets):
|
|
|
105
133
|
)
|
|
106
134
|
raise SystemExit(1)
|
|
107
135
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
click.secho(
|
|
116
|
-
f" docker the {target.label} mirror is left out, its login failed "
|
|
117
|
-
f"or {' / '.join(target.credential_names())} are not set",
|
|
118
|
-
fg="yellow",
|
|
119
|
-
)
|
|
120
|
-
continue
|
|
121
|
-
user_env, secret_env = target.credential_names()
|
|
122
|
-
click.secho(
|
|
123
|
-
f" error: could not log into {target.label}. Check {user_env} and "
|
|
124
|
-
f"{secret_env}.",
|
|
125
|
-
fg="red",
|
|
126
|
-
)
|
|
127
|
-
raise SystemExit(1)
|
|
128
|
-
|
|
129
|
-
refs = [ref for target in usable for ref in target.refs]
|
|
130
|
-
click.echo(" docker building image...")
|
|
131
|
-
try:
|
|
132
|
-
build_production_image(product, str(context.workspace()), docker_dir, refs)
|
|
133
|
-
except subprocess.CalledProcessError:
|
|
134
|
-
click.secho(" error: Docker image build failed.", fg="red")
|
|
135
|
-
raise SystemExit(1)
|
|
136
|
-
|
|
137
|
-
for target in usable:
|
|
138
|
-
click.echo(f" docker pushing to {target.label}...")
|
|
139
|
-
try:
|
|
140
|
-
for ref in target.refs:
|
|
141
|
-
run(["docker", "push", ref])
|
|
142
|
-
except subprocess.CalledProcessError:
|
|
136
|
+
with registry_session([t.host for t in targets]) as docker_env:
|
|
137
|
+
usable = []
|
|
138
|
+
for target in targets:
|
|
139
|
+
click.echo(f" docker logging into {target.label}...")
|
|
140
|
+
if _login(target, docker_env):
|
|
141
|
+
usable.append(target)
|
|
142
|
+
continue
|
|
143
143
|
if target.mirror:
|
|
144
144
|
click.secho(
|
|
145
|
-
f" docker the
|
|
146
|
-
"
|
|
145
|
+
f" docker the {target.label} mirror is left out, its login "
|
|
146
|
+
f"failed or {' / '.join(target.credential_names())} are not set",
|
|
147
147
|
fg="yellow",
|
|
148
148
|
)
|
|
149
149
|
continue
|
|
150
|
-
|
|
150
|
+
user_env, secret_env = target.credential_names()
|
|
151
|
+
click.secho(
|
|
152
|
+
f" error: could not log into {target.label}. Check {user_env} and "
|
|
153
|
+
f"{secret_env}.",
|
|
154
|
+
fg="red",
|
|
155
|
+
)
|
|
156
|
+
raise SystemExit(1)
|
|
157
|
+
|
|
158
|
+
refs = [ref for target in usable for ref in target.refs]
|
|
159
|
+
click.echo(" docker building image...")
|
|
160
|
+
try:
|
|
161
|
+
build_production_image(product, str(context.workspace()), docker_dir, refs)
|
|
162
|
+
except subprocess.CalledProcessError:
|
|
163
|
+
click.secho(" error: Docker image build failed.", fg="red")
|
|
151
164
|
raise SystemExit(1)
|
|
152
|
-
|
|
165
|
+
|
|
166
|
+
for target in usable:
|
|
167
|
+
click.echo(f" docker pushing to {target.label}...")
|
|
168
|
+
try:
|
|
169
|
+
for ref in target.refs:
|
|
170
|
+
run(["docker", "push", ref], env=docker_env)
|
|
171
|
+
except subprocess.CalledProcessError:
|
|
172
|
+
if target.mirror:
|
|
173
|
+
click.secho(
|
|
174
|
+
f" docker the push to the {target.label} mirror failed "
|
|
175
|
+
"and is left out",
|
|
176
|
+
fg="yellow",
|
|
177
|
+
)
|
|
178
|
+
continue
|
|
179
|
+
click.secho(f" error: the push to {target.label} failed.", fg="red")
|
|
180
|
+
raise SystemExit(1)
|
|
181
|
+
click.echo(f" docker {', '.join(target.refs)}")
|
|
153
182
|
|
|
154
183
|
click.echo(" docker push complete")
|
|
155
184
|
|
|
@@ -19,7 +19,8 @@ Layout of the index document (schema 1)::
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
Feature entries carry the full contract (provides / requires / extensible /
|
|
22
|
-
docker), the
|
|
22
|
+
docker), the settings the feature declares under ``[tool.splent.config]``
|
|
23
|
+
(``config``), the derived archetype, presentation metadata (description,
|
|
23
24
|
category, tags), distribution state (latest tag, PyPI presence) and the
|
|
24
25
|
computed reverse dependencies (``used_by``).
|
|
25
26
|
"""
|
|
@@ -36,6 +37,11 @@ import click
|
|
|
36
37
|
INDEX_SCHEMA = 1
|
|
37
38
|
FEATURE_PREFIX = "splent_feature_"
|
|
38
39
|
|
|
40
|
+
# Where a feature declares the variables it reads, and the value a setting
|
|
41
|
+
# holds when only the deployment can supply it (a secret).
|
|
42
|
+
CONFIG_TABLE = ("tool", "splent", "config")
|
|
43
|
+
REQUIRED_VALUE = "<SET>"
|
|
44
|
+
|
|
39
45
|
# Feeds feature:search / feature:info when no explicit path is given.
|
|
40
46
|
INDEX_URL_ENV = "SPLENT_INDEX_URL"
|
|
41
47
|
|
|
@@ -72,6 +78,67 @@ def index_cache_path(workspace: str) -> Path:
|
|
|
72
78
|
# ── Feature entries ───────────────────────────────────────────────────
|
|
73
79
|
|
|
74
80
|
|
|
81
|
+
# The value types product:env writes into an env file. A table or a date
|
|
82
|
+
# under [tool.splent.config] never reaches the environment, so it is no
|
|
83
|
+
# setting a product can change.
|
|
84
|
+
_CONFIG_TYPES = (bool, int, float, str, list)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _config_text(value) -> str:
|
|
88
|
+
"""A declared value as the environment receives it from ``product:env``.
|
|
89
|
+
|
|
90
|
+
Booleans are ``true``/``false``, numbers and strings their plain text, and
|
|
91
|
+
a list its items joined by commas.
|
|
92
|
+
"""
|
|
93
|
+
if isinstance(value, bool):
|
|
94
|
+
return "true" if value else "false"
|
|
95
|
+
if isinstance(value, list):
|
|
96
|
+
return ",".join(_config_text(item) for item in value)
|
|
97
|
+
return str(value)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def config_declarations(pyproject_text: str, data: dict | None = None) -> list[dict]:
|
|
101
|
+
"""The settings a feature declares under ``[tool.splent.config]``.
|
|
102
|
+
|
|
103
|
+
One ``{"key", "default", "required", "help"}`` per key, in the order they
|
|
104
|
+
are declared (tomllib keeps it), for every key whose value reaches the
|
|
105
|
+
environment. ``default`` is the value as text (see :func:`_config_text`).
|
|
106
|
+
A value of ``<SET>`` means only the deployment can supply it, so it gives
|
|
107
|
+
``required`` true and an empty ``default``. ``help`` is the comment above
|
|
108
|
+
the key, or trailing its value (see :func:`toml_edit.key_comments`), and
|
|
109
|
+
``""`` when there is none.
|
|
110
|
+
|
|
111
|
+
The values come from tomllib (*data*, parsed from *pyproject_text* when
|
|
112
|
+
not given) and only the comments from the text, so an unusual layout
|
|
113
|
+
costs the help and never a key or a value.
|
|
114
|
+
"""
|
|
115
|
+
from splent_cli.utils.toml_edit import TomlEditError, key_comments
|
|
116
|
+
|
|
117
|
+
if data is None:
|
|
118
|
+
data = tomllib.loads(pyproject_text)
|
|
119
|
+
declared = data.get("tool", {}).get("splent", {}).get("config", {})
|
|
120
|
+
if not isinstance(declared, dict) or not declared:
|
|
121
|
+
return []
|
|
122
|
+
try:
|
|
123
|
+
comments = key_comments(pyproject_text, CONFIG_TABLE)
|
|
124
|
+
except TomlEditError:
|
|
125
|
+
comments = {}
|
|
126
|
+
settings = []
|
|
127
|
+
for key, value in declared.items():
|
|
128
|
+
if not isinstance(value, _CONFIG_TYPES):
|
|
129
|
+
continue
|
|
130
|
+
required = isinstance(value, str) and value.strip() == REQUIRED_VALUE
|
|
131
|
+
settings.append(
|
|
132
|
+
{
|
|
133
|
+
"key": key,
|
|
134
|
+
"default": "" if required else _config_text(value),
|
|
135
|
+
"required": required,
|
|
136
|
+
"help": comments.get(key, ""),
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
return settings
|
|
140
|
+
|
|
141
|
+
|
|
75
142
|
def feature_entry_from_pyproject(
|
|
76
143
|
pyproject_text: str,
|
|
77
144
|
*,
|
|
@@ -109,6 +176,7 @@ def feature_entry_from_pyproject(
|
|
|
109
176
|
"provides": contract.get("provides", {}),
|
|
110
177
|
"requires": contract.get("requires", {}),
|
|
111
178
|
"extensible": contract.get("extensible", {}),
|
|
179
|
+
"config": config_declarations(pyproject_text, data),
|
|
112
180
|
"docker": contract.get("docker") or None,
|
|
113
181
|
"refinement": splent.get("refinement") or None,
|
|
114
182
|
"cli_version": splent.get("cli_version"),
|
|
@@ -203,11 +203,33 @@ def require_release_version(version: str | None) -> None:
|
|
|
203
203
|
raise SystemExit(1)
|
|
204
204
|
|
|
205
205
|
|
|
206
|
-
def
|
|
207
|
-
"""
|
|
206
|
+
def untracked_files(cwd: str) -> set[str]:
|
|
207
|
+
"""Paths git does not track and does not ignore, relative to ``cwd``."""
|
|
208
|
+
r = subprocess.run(
|
|
209
|
+
["git", "ls-files", "--others", "--exclude-standard"],
|
|
210
|
+
capture_output=True,
|
|
211
|
+
text=True,
|
|
212
|
+
cwd=cwd,
|
|
213
|
+
)
|
|
214
|
+
return {line for line in r.stdout.splitlines() if line.strip()}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def commit_locally(
|
|
218
|
+
cwd: str,
|
|
219
|
+
version: str,
|
|
220
|
+
subject: str = "bump version",
|
|
221
|
+
leave_out: set[str] | None = None,
|
|
222
|
+
) -> None:
|
|
223
|
+
"""Stage the release's changes and commit. Nothing leaves the machine here.
|
|
208
224
|
|
|
209
225
|
A local commit is reversible, so it happens before the first irreversible
|
|
210
226
|
step and is pushed only once the un-undoable channel has accepted.
|
|
227
|
+
|
|
228
|
+
``leave_out`` are the untracked files that were already there before the
|
|
229
|
+
release started. They are not staged: the marketplace's first release
|
|
230
|
+
committed a derive log, the staged UVL model and the contracts snapshot,
|
|
231
|
+
all of them build output the developer never meant to version, and the
|
|
232
|
+
server then refused to update over its own copies of them.
|
|
211
233
|
"""
|
|
212
234
|
r = subprocess.run(
|
|
213
235
|
["git", "status", "--porcelain"],
|
|
@@ -219,10 +241,34 @@ def commit_locally(cwd: str, version: str, subject: str = "bump version") -> Non
|
|
|
219
241
|
click.echo(" commit working tree clean, nothing to commit")
|
|
220
242
|
return
|
|
221
243
|
|
|
244
|
+
leave_out = set(leave_out or ())
|
|
245
|
+
if leave_out:
|
|
246
|
+
click.secho(
|
|
247
|
+
f" commit leaving {len(leave_out)} file(s) out that were untracked "
|
|
248
|
+
"before the release: "
|
|
249
|
+
+ ", ".join(sorted(leave_out)[:5])
|
|
250
|
+
+ (" ..." if len(leave_out) > 5 else ""),
|
|
251
|
+
fg="bright_black",
|
|
252
|
+
)
|
|
222
253
|
try:
|
|
223
254
|
subprocess.run(
|
|
224
|
-
["git", "add", "-
|
|
255
|
+
["git", "add", "-u"], cwd=cwd, check=True, capture_output=True, text=True
|
|
256
|
+
)
|
|
257
|
+
created = sorted(untracked_files(cwd) - leave_out)
|
|
258
|
+
if created:
|
|
259
|
+
subprocess.run(
|
|
260
|
+
["git", "add", "--", *created],
|
|
261
|
+
cwd=cwd,
|
|
262
|
+
check=True,
|
|
263
|
+
capture_output=True,
|
|
264
|
+
text=True,
|
|
265
|
+
)
|
|
266
|
+
staged = subprocess.run(
|
|
267
|
+
["git", "diff", "--cached", "--quiet"], cwd=cwd, capture_output=True
|
|
225
268
|
)
|
|
269
|
+
if staged.returncode == 0:
|
|
270
|
+
click.echo(" commit nothing the release changed, nothing to commit")
|
|
271
|
+
return
|
|
226
272
|
subprocess.run(
|
|
227
273
|
["git", "commit", "-m", f"chore: {subject} to {version}"],
|
|
228
274
|
cwd=cwd,
|
|
@@ -1598,12 +1644,16 @@ def run_release_pipeline(
|
|
|
1598
1644
|
# ── Everything below this line changes the world ──────────────────
|
|
1599
1645
|
click.echo()
|
|
1600
1646
|
|
|
1647
|
+
# Files nobody versioned before the release started are the developer's
|
|
1648
|
+
# business (a log, a local note), not the release's.
|
|
1649
|
+
left_alone = untracked_files(path)
|
|
1650
|
+
|
|
1601
1651
|
update_version(pyproject, normalized)
|
|
1602
1652
|
|
|
1603
1653
|
if pre_commit_hook:
|
|
1604
1654
|
pre_commit_hook(path, normalized)
|
|
1605
1655
|
|
|
1606
|
-
commit_locally(path, tag, subject=f"release {name}")
|
|
1656
|
+
commit_locally(path, tag, subject=f"release {name}", leave_out=left_alone)
|
|
1607
1657
|
|
|
1608
1658
|
# Where this version is already published. From the first entry on, any
|
|
1609
1659
|
# failure leaves the channels out of step, and is reported as such.
|
|
@@ -32,6 +32,10 @@ nothing is written. The same line by line idea is what
|
|
|
32
32
|
``upgrade_plan.replace_feature_pins`` does for ``feature:upgrade``; this module
|
|
33
33
|
covers the edits that insert and remove entries, keys and tables as well.
|
|
34
34
|
|
|
35
|
+
:func:`key_comments` reads the same scan the other way round. It returns
|
|
36
|
+
the comment written above each key of a table, which is how a feature
|
|
37
|
+
documents its settings, so the marketplace can show it.
|
|
38
|
+
|
|
35
39
|
No third party round-trip library is needed. tomlkit would keep the
|
|
36
40
|
formatting too, but it is one more dependency every CLI install and image
|
|
37
41
|
has to carry, and replacing a list through it still loses the comments
|
|
@@ -742,6 +746,50 @@ def edit_toml(text: str, data: dict) -> str:
|
|
|
742
746
|
return result
|
|
743
747
|
|
|
744
748
|
|
|
749
|
+
def key_comments(text: str, table: tuple[str, ...]) -> dict[str, str]:
|
|
750
|
+
"""The comment that documents each key written directly in *table*.
|
|
751
|
+
|
|
752
|
+
A key's comment is the block of ``#`` lines right above it, each line
|
|
753
|
+
without its leading ``#`` and joined into one paragraph. When there is no
|
|
754
|
+
block, it is the comment trailing the key's value instead. A blank line
|
|
755
|
+
ends a block, so a comment separated from the key by one documents
|
|
756
|
+
something else, and so does one above the table header. A key with
|
|
757
|
+
neither maps to ``""``. Keys of sub-tables and of arrays of tables are
|
|
758
|
+
left out.
|
|
759
|
+
|
|
760
|
+
Raises :class:`TomlEditError` for a file the scanner cannot read.
|
|
761
|
+
"""
|
|
762
|
+
doc = _Scanner(text).scan()
|
|
763
|
+
# By TOML's grammar, what lies between the end of one statement and the
|
|
764
|
+
# start of the next is only blank lines and comments.
|
|
765
|
+
ends = sorted(
|
|
766
|
+
[t.header_end for t in doc.tables[1:]] + [kv.line_end for kv in doc.kvs]
|
|
767
|
+
)
|
|
768
|
+
depth = len(table) + 1
|
|
769
|
+
out: dict[str, str] = {}
|
|
770
|
+
for kv in doc.kvs:
|
|
771
|
+
if kv.in_aot or len(kv.path) != depth or kv.path[:-1] != table:
|
|
772
|
+
continue
|
|
773
|
+
gap_start = max((end for end in ends if end <= kv.line_start), default=0)
|
|
774
|
+
block: list[str] = []
|
|
775
|
+
for line in text[gap_start : kv.line_start].splitlines():
|
|
776
|
+
stripped = line.lstrip("\ufeff").strip()
|
|
777
|
+
block = (
|
|
778
|
+
[*block, _comment_text(stripped)] if stripped.startswith("#") else []
|
|
779
|
+
)
|
|
780
|
+
comment = " ".join(part for part in block if part)
|
|
781
|
+
if not comment:
|
|
782
|
+
tail = text[kv.value_end : kv.line_end].strip()
|
|
783
|
+
comment = _comment_text(tail) if tail.startswith("#") else ""
|
|
784
|
+
out[kv.path[-1]] = comment
|
|
785
|
+
return out
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def _comment_text(line: str) -> str:
|
|
789
|
+
"""A comment line without its leading ``#`` marks and spaces."""
|
|
790
|
+
return line.lstrip("#").strip()
|
|
791
|
+
|
|
792
|
+
|
|
745
793
|
def write_toml(path: str | os.PathLike, data: dict) -> None:
|
|
746
794
|
"""Write *data* to the TOML file at *path*, keeping its comments and layout.
|
|
747
795
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: splent_cli
|
|
3
|
-
Version: 1.29.
|
|
3
|
+
Version: 1.29.1
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|