splent-cli 1.54.0__tar.gz → 1.55.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.54.0/src/splent_cli.egg-info → splent_cli-1.55.0}/PKG-INFO +1 -1
- {splent_cli-1.54.0 → splent_cli-1.55.0}/pyproject.toml +1 -1
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pip_install.py +198 -3
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +108 -45
- {splent_cli-1.54.0 → splent_cli-1.55.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.54.0 → splent_cli-1.55.0}/COPYING +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/COPYING.LESSER +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/README.md +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/setup.cfg +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/check/check_vendor.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_frontend_init.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_npm.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_release.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_security.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_build.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_frontend_audit.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_params.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_release.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_ui_audit.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/product/product_validate.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/release/release_resume.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/storage/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/storage/storage_migrate.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/tokens_setup.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/deploy_bundle.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/editable_refresh.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/frontend.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/image_registry.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/neutral_app.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/own_commands.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/preflight.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/pypi_quota.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/registry.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/release.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/release_dry_run.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/release_gate.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/release_workspace.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/security_scan.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/source_mirror.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/spl_store.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/ui_audit.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/services/web_container.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_.gitignore.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/licenses/COPYING +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.env.prod.example.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_400.html +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_401.html +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_404.html +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_500.html +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_app.css +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_app.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_base_template.html +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_dropzone.css +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_favicon.svg +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_own.css +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/ui_audit/measure.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/Dockerfile.frontend +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/package-lock.json +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/package.json +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/build.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/legacy.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/lint.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/plugin.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/preset.js +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/toolchain/frontend/splent/singletons.json +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/ci_services.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/ci_workflow.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/db_client.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/deploy_freshness.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_naming.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_params.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_requirements.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_resolution.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/forge.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/frontend_check.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/frontend_licenses.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/menu_keys.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/migration_lock.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/npm_semver.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/redact.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/refinement_facts.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/running_version.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/toml_edit.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/upgrade_plan.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/utils/uvl_params.py +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/SOURCES.txt +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli.egg-info/top_level.txt +0 -0
{splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/commands/feature/feature_pip_install.py
RENAMED
|
@@ -15,6 +15,13 @@ and because a private feature may deliberately never be published there.
|
|
|
15
15
|
Without the fallback, one unpublished feature makes the whole product
|
|
16
16
|
unbuildable even though its code is tagged and reachable.
|
|
17
17
|
|
|
18
|
+
Everything is installed by one pip run. Which channel serves each feature is
|
|
19
|
+
settled first, asking PyPI and the forge for all of them at once, and a tag is
|
|
20
|
+
named by the commit it points at: pip keeps the wheel it builds from a commit
|
|
21
|
+
in its cache, and the next build takes it from there instead of cloning and
|
|
22
|
+
building the feature again. Only when that run fails are the features
|
|
23
|
+
installed one at a time, which says which one failed and why.
|
|
24
|
+
|
|
18
25
|
Three things about this are worth stating plainly rather than discovering:
|
|
19
26
|
|
|
20
27
|
* only [tool.splent].features is read. features_dev and features_prod are
|
|
@@ -39,7 +46,9 @@ Example:
|
|
|
39
46
|
"""
|
|
40
47
|
|
|
41
48
|
import os
|
|
49
|
+
import subprocess
|
|
42
50
|
import sys
|
|
51
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
43
52
|
|
|
44
53
|
import click
|
|
45
54
|
import tomllib
|
|
@@ -154,6 +163,180 @@ def _last_line(output: str) -> str:
|
|
|
154
163
|
return _redacted(lines[-1]) if lines else "no output"
|
|
155
164
|
|
|
156
165
|
|
|
166
|
+
# How many PyPI and forge questions are asked at once.
|
|
167
|
+
PROBE_WORKERS = 16
|
|
168
|
+
|
|
169
|
+
# Seconds one question to PyPI or one ``git ls-remote`` may take.
|
|
170
|
+
PROBE_TIMEOUT = 20
|
|
171
|
+
|
|
172
|
+
# What a failed pip run is summed up with, counted from its end.
|
|
173
|
+
TAIL_LINES = 15
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _asks_pypi() -> bool:
|
|
177
|
+
"""Whether pip installs from PyPI, so PyPI is the one to ask.
|
|
178
|
+
|
|
179
|
+
A PIP_INDEX_URL that points elsewhere (a mirror, a private index) serves
|
|
180
|
+
what PyPI does not say, so nothing is asked and the features are
|
|
181
|
+
installed one at a time, each trying its index first.
|
|
182
|
+
"""
|
|
183
|
+
index = os.getenv("PIP_INDEX_URL", "").strip()
|
|
184
|
+
return not index or index.rstrip("/") in (
|
|
185
|
+
"https://pypi.org/simple",
|
|
186
|
+
"https://pypi.python.org/simple",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _pypi_serves(dist: str, version: str) -> bool | None:
|
|
191
|
+
"""Whether PyPI serves ``dist==version``, None when it could not be asked."""
|
|
192
|
+
import requests
|
|
193
|
+
|
|
194
|
+
url = f"https://pypi.org/pypi/{dist}/{version}/json"
|
|
195
|
+
for _attempt in range(2):
|
|
196
|
+
try:
|
|
197
|
+
response = requests.get(url, timeout=PROBE_TIMEOUT)
|
|
198
|
+
except requests.RequestException:
|
|
199
|
+
continue
|
|
200
|
+
if response.status_code == 200:
|
|
201
|
+
return True
|
|
202
|
+
if response.status_code == 404:
|
|
203
|
+
return False
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _tag_commit(namespace: str, name: str, ref: str) -> tuple[str, str, str] | None:
|
|
208
|
+
"""(real_url, display_url, commit) of the tag, from the first spelling of
|
|
209
|
+
the namespace that has it, or None.
|
|
210
|
+
|
|
211
|
+
Asked with ``git ls-remote``, so nothing is cloned. An annotated tag is
|
|
212
|
+
named by the commit it points at, not by the tag object.
|
|
213
|
+
"""
|
|
214
|
+
env = {
|
|
215
|
+
**os.environ,
|
|
216
|
+
**forge.git_auth_env(),
|
|
217
|
+
"GIT_TERMINAL_PROMPT": "0",
|
|
218
|
+
"LC_ALL": "C",
|
|
219
|
+
}
|
|
220
|
+
for spelling in namespace_spellings(namespace):
|
|
221
|
+
real, display = https_url(spelling, name)
|
|
222
|
+
try:
|
|
223
|
+
result = subprocess.run(
|
|
224
|
+
["git", "ls-remote", real, f"refs/tags/{ref}", f"refs/tags/{ref}^{{}}"],
|
|
225
|
+
capture_output=True,
|
|
226
|
+
text=True,
|
|
227
|
+
timeout=PROBE_TIMEOUT,
|
|
228
|
+
env=env,
|
|
229
|
+
)
|
|
230
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
231
|
+
continue
|
|
232
|
+
if result.returncode != 0:
|
|
233
|
+
continue
|
|
234
|
+
refs = dict(
|
|
235
|
+
reversed(line.split("\t", 1))
|
|
236
|
+
for line in result.stdout.splitlines()
|
|
237
|
+
if "\t" in line
|
|
238
|
+
)
|
|
239
|
+
commit = refs.get(f"refs/tags/{ref}^{{}}") or refs.get(f"refs/tags/{ref}")
|
|
240
|
+
if commit:
|
|
241
|
+
return real, display, commit
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _pip_install_many(specs: list[str]) -> tuple[bool, str]:
|
|
246
|
+
"""One pip run for every requirement in ``specs``.
|
|
247
|
+
|
|
248
|
+
What pip says is shown as it says it, redacted line by line, so a long
|
|
249
|
+
build is never silent. Returns whether it succeeded and its last lines.
|
|
250
|
+
"""
|
|
251
|
+
auth = forge.git_auth_env()
|
|
252
|
+
cmd = [sys.executable, "-m", "pip", "install", "--progress-bar", "off", *specs]
|
|
253
|
+
tail: list[str] = []
|
|
254
|
+
try:
|
|
255
|
+
process = subprocess.Popen(
|
|
256
|
+
cmd,
|
|
257
|
+
stdout=subprocess.PIPE,
|
|
258
|
+
stderr=subprocess.STDOUT,
|
|
259
|
+
text=True,
|
|
260
|
+
env={**os.environ, **auth} if auth else None,
|
|
261
|
+
)
|
|
262
|
+
except OSError as exc:
|
|
263
|
+
return False, str(exc)
|
|
264
|
+
assert process.stdout is not None
|
|
265
|
+
for line in process.stdout:
|
|
266
|
+
line = _redacted(line.rstrip("\n"))
|
|
267
|
+
click.echo(f" {line}")
|
|
268
|
+
tail = [*tail, line][-TAIL_LINES:]
|
|
269
|
+
return process.wait() == 0, "\n".join(tail)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _requirement(entry, data, pypi_only, asks_pypi):
|
|
273
|
+
"""What one pip run installs for ``entry``.
|
|
274
|
+
|
|
275
|
+
Returns (spec, display, from_git), or None when it cannot be settled
|
|
276
|
+
without trying (PyPI could not be asked, or its tag could not be found),
|
|
277
|
+
which leaves the entry to the one-at-a-time install.
|
|
278
|
+
"""
|
|
279
|
+
namespace, name, version, ref = _parse_feature_entry(entry)
|
|
280
|
+
dist = feature_distribution(data, name)
|
|
281
|
+
if not version:
|
|
282
|
+
return dist, dist, None
|
|
283
|
+
spec = f"{dist}=={version}"
|
|
284
|
+
if pypi_only:
|
|
285
|
+
return spec, spec, None
|
|
286
|
+
if not (ref and namespace):
|
|
287
|
+
return spec, spec, None
|
|
288
|
+
if not asks_pypi:
|
|
289
|
+
return None
|
|
290
|
+
served = _pypi_serves(dist, version)
|
|
291
|
+
if served is None:
|
|
292
|
+
return None
|
|
293
|
+
if served:
|
|
294
|
+
return spec, spec, None
|
|
295
|
+
found = _tag_commit(namespace, name, ref)
|
|
296
|
+
if found is None:
|
|
297
|
+
return None
|
|
298
|
+
real, display, commit = found
|
|
299
|
+
return (
|
|
300
|
+
f"{dist} @ git+{real}@{commit}",
|
|
301
|
+
f"git+{display}@{ref} ({commit[:12]})",
|
|
302
|
+
f"{name} {ref}",
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _install_together(features, data, pypi_only) -> list[str] | None:
|
|
307
|
+
"""Install every feature in one pip run.
|
|
308
|
+
|
|
309
|
+
Returns what came from a git tag, or None when the features have to be
|
|
310
|
+
installed one at a time: one of them could not be settled in advance, or
|
|
311
|
+
the run failed and the one-at-a-time install says which feature broke it.
|
|
312
|
+
"""
|
|
313
|
+
asks_pypi = _asks_pypi()
|
|
314
|
+
with ThreadPoolExecutor(max_workers=PROBE_WORKERS) as pool:
|
|
315
|
+
plan = list(
|
|
316
|
+
pool.map(
|
|
317
|
+
lambda entry: _requirement(entry, data, pypi_only, asks_pypi),
|
|
318
|
+
features,
|
|
319
|
+
)
|
|
320
|
+
)
|
|
321
|
+
if any(item is None for item in plan):
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
for _spec, display, from_git in plan:
|
|
325
|
+
click.echo(f" {'git ' if from_git else 'pypi'} {display}")
|
|
326
|
+
click.echo()
|
|
327
|
+
ok, tail = _pip_install_many([spec for spec, _display, _git in plan])
|
|
328
|
+
click.echo()
|
|
329
|
+
if not ok:
|
|
330
|
+
click.secho(
|
|
331
|
+
" Installing them together failed. Installing them one at a time, "
|
|
332
|
+
"to say which one:",
|
|
333
|
+
fg="yellow",
|
|
334
|
+
)
|
|
335
|
+
click.echo()
|
|
336
|
+
return None
|
|
337
|
+
return [from_git for _spec, _display, from_git in plan if from_git]
|
|
338
|
+
|
|
339
|
+
|
|
157
340
|
@click.command(
|
|
158
341
|
"feature:pip-install",
|
|
159
342
|
short_help="Install the declared features from PyPI, or from their git tag.",
|
|
@@ -204,15 +387,22 @@ def feature_pip_install(pypi_only):
|
|
|
204
387
|
|
|
205
388
|
click.echo(f" Installing {len(features)} feature(s)...\n")
|
|
206
389
|
|
|
390
|
+
unpinned = [
|
|
391
|
+
_parse_feature_entry(entry)[1]
|
|
392
|
+
for entry in features
|
|
393
|
+
if not _parse_feature_entry(entry)[2]
|
|
394
|
+
]
|
|
395
|
+
together = _install_together(features, data, pypi_only)
|
|
396
|
+
if together is not None:
|
|
397
|
+
_summary(features, unpinned, together, [])
|
|
398
|
+
return
|
|
399
|
+
|
|
207
400
|
failed = []
|
|
208
|
-
unpinned = []
|
|
209
401
|
from_git = []
|
|
210
402
|
for entry in features:
|
|
211
403
|
namespace, name, version, ref = _parse_feature_entry(entry)
|
|
212
404
|
dist = feature_distribution(data, name)
|
|
213
405
|
spec = f"{dist}=={version}" if version else dist
|
|
214
|
-
if not version:
|
|
215
|
-
unpinned.append(name)
|
|
216
406
|
|
|
217
407
|
click.echo(f" installing {spec}")
|
|
218
408
|
|
|
@@ -250,6 +440,11 @@ def feature_pip_install(pypi_only):
|
|
|
250
440
|
)
|
|
251
441
|
failed.append(spec)
|
|
252
442
|
|
|
443
|
+
_summary(features, unpinned, from_git, failed)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _summary(features, unpinned, from_git, failed) -> None:
|
|
447
|
+
"""What was installed from where, and what failed (which exits 1)."""
|
|
253
448
|
click.echo()
|
|
254
449
|
if unpinned:
|
|
255
450
|
click.secho(
|
{splent_cli-1.54.0 → splent_cli-1.55.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2
RENAMED
|
@@ -11,12 +11,20 @@
|
|
|
11
11
|
# writes those pins, and the two files the image reads, splent.contracts.json
|
|
12
12
|
# and docker/spl/. A clone of the repository builds the same image a release
|
|
13
13
|
# published.
|
|
14
|
-
|
|
15
|
-
#
|
|
14
|
+
#
|
|
15
|
+
# The image is built in layers that change at different paces, and each is
|
|
16
|
+
# only rebuilt when what it is made from changes: the third-party packages
|
|
17
|
+
# when the lock does, the framework, the CLI and the features when their
|
|
18
|
+
# pins do, the product with every commit. pip keeps what it downloads and
|
|
19
|
+
# builds in a BuildKit cache, so a layer that is rebuilt takes from there
|
|
20
|
+
# what an earlier build already fetched or compiled. A server that updates
|
|
21
|
+
# the image pulls only the layers that changed.
|
|
22
|
+
|
|
23
|
+
# ── Stage 1: Third-party packages ────────────────────────────────────────────
|
|
16
24
|
# Both stages on the same Debian release, so the packages the builder compiles
|
|
17
25
|
# run on the runtime's libraries. The Python patch release follows the image,
|
|
18
26
|
# which is how its security fixes arrive.
|
|
19
|
-
FROM python:3.13-trixie AS
|
|
27
|
+
FROM python:3.13-trixie AS deps
|
|
20
28
|
WORKDIR /workspace
|
|
21
29
|
|
|
22
30
|
# System dependencies for building
|
|
@@ -39,58 +47,70 @@ RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /opt/node/bin/npm \
|
|
|
39
47
|
&& ln -s ../lib/node_modules/npm/bin/npx-cli.js /opt/node/bin/npx
|
|
40
48
|
ENV PATH=/opt/node/bin:$PATH
|
|
41
49
|
|
|
42
|
-
# The product. Dockerfile.{{ product_name }}.prod.dockerignore keeps the env
|
|
43
|
-
# files (credentials), the development links and the deploy bundle out.
|
|
44
|
-
COPY . {{ product_name }}/
|
|
45
|
-
|
|
46
|
-
# Same protection for a builder that ignores that file (the classic builder,
|
|
47
|
-
# DOCKER_BUILDKIT=0). Only the runtime stage is pushed, and it copies
|
|
48
|
-
# /workspace from this stage, so the env files never reach a published image.
|
|
49
|
-
RUN find {{ product_name }}/docker -maxdepth 1 -name '.env*' ! -name '*.example' \
|
|
50
|
-
\( -type f -o -type l \) -delete
|
|
51
|
-
|
|
52
|
-
# The SPL's UVL model. The framework orders features, and their migrations,
|
|
53
|
-
# by its constraints and looks for it in .splent_cache/spls/. splent
|
|
54
|
-
# product:build keeps the model pyproject.toml pins in docker/spl/, and this
|
|
55
|
-
# puts it where the framework and the CLI look. Without it every boot warns
|
|
56
|
-
# that features load in pyproject.toml order.
|
|
57
|
-
RUN if [ -d {{ product_name }}/docker/spl ]; then \
|
|
58
|
-
mkdir -p .splent_cache/spls && \
|
|
59
|
-
cp -R {{ product_name }}/docker/spl/. .splent_cache/spls/; \
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
50
|
ENV PIP_ROOT_USER_ACTION=ignore
|
|
63
51
|
|
|
64
52
|
# Every third-party package at the version docker/requirements.lock pins, as
|
|
65
53
|
# a constraint on every pip install below, so a clone of a release builds the
|
|
66
54
|
# set the release built. splent product:build and product:release write the
|
|
67
55
|
# lock by building with SPLENT_FRESH_LOCK=1, which resolves without it.
|
|
56
|
+
COPY docker/requirements.loc[k] /tmp/lock/
|
|
68
57
|
ARG SPLENT_FRESH_LOCK=""
|
|
69
|
-
RUN if [ -n "$SPLENT_FRESH_LOCK" ] || [ ! -f
|
|
58
|
+
RUN if [ -n "$SPLENT_FRESH_LOCK" ] || [ ! -f /tmp/lock/requirements.lock ]; then \
|
|
70
59
|
: > /tmp/constraints.txt; \
|
|
71
60
|
else \
|
|
72
|
-
cp
|
|
61
|
+
cp /tmp/lock/requirements.lock /tmp/constraints.txt; \
|
|
73
62
|
fi
|
|
74
63
|
ENV PIP_CONSTRAINT=/tmp/constraints.txt
|
|
75
64
|
|
|
65
|
+
# The packages the lock pins, installed before anything SPLENT, so this layer
|
|
66
|
+
# only changes with the lock.
|
|
67
|
+
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
68
|
+
if grep -q '^[^#[:space:]]' /tmp/constraints.txt; then \
|
|
69
|
+
pip install -r /tmp/constraints.txt; \
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ── Stage 2: Pins ────────────────────────────────────────────────────────────
|
|
73
|
+
# What the builder installs from pyproject.toml, and nothing else of it: the
|
|
74
|
+
# requirements of [tool.splent.runtime] and [project].dependencies, and the
|
|
75
|
+
# features of [tool.splent]. Its version and settings change with every
|
|
76
|
+
# release, and the builder then reinstalls nothing, because these files are
|
|
77
|
+
# the same.
|
|
78
|
+
FROM python:3.13-trixie AS pins
|
|
79
|
+
WORKDIR /pins
|
|
80
|
+
COPY pyproject.toml /tmp/pyproject.toml
|
|
81
|
+
|
|
76
82
|
# The framework, the CLI and the WSGI server, at the versions
|
|
77
|
-
# [tool.splent.runtime]
|
|
78
|
-
#
|
|
79
|
-
# versions the product is built with, and a
|
|
80
|
-
# one PyPI does not serve.
|
|
83
|
+
# [tool.splent.runtime] pins (a bare version means exactly that version),
|
|
84
|
+
# then what the product itself depends on. splent product:build keeps the
|
|
85
|
+
# framework and the CLI at the versions the product is built with, and a
|
|
86
|
+
# release of the product refuses one PyPI does not serve.
|
|
81
87
|
RUN python -c "import sys, tomllib; \
|
|
82
|
-
|
|
88
|
+
data = tomllib.load(open('/tmp/pyproject.toml', 'rb')); \
|
|
89
|
+
runtime = data.get('tool', {}).get('splent', {}).get('runtime') \
|
|
83
90
|
or sys.exit('[tool.splent.runtime] is missing from pyproject.toml. Run splent product:build.'); \
|
|
84
|
-
print('\\n'.join(name + ('==' + str(spec) if str(spec)[:1].isdigit() else str(spec)) for name, spec in runtime.items()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
print('\\n'.join([name + ('==' + str(spec) if str(spec)[:1].isdigit() else str(spec)) for name, spec in runtime.items()] \
|
|
92
|
+
+ list(data.get('project', {}).get('dependencies', []))))" \
|
|
93
|
+
> /pins/runtime.txt
|
|
94
|
+
|
|
95
|
+
# The features, with the PyPI names of those published under one of their
|
|
96
|
+
# own, as feature:pip-install reads them.
|
|
97
|
+
RUN python -c "import json, tomllib; \
|
|
98
|
+
splent = tomllib.load(open('/tmp/pyproject.toml', 'rb')).get('tool', {}).get('splent', {}); \
|
|
99
|
+
lines = ['[tool.splent]'] + [key + ' = ' + json.dumps(splent[key]) for key in ('features', 'features_dev', 'features_prod') if key in splent]; \
|
|
100
|
+
lines += ['[tool.splent.distributions]'] + [json.dumps(k) + ' = ' + json.dumps(v) for k, v in (splent.get('distributions') or {}).items()]; \
|
|
101
|
+
print('\\n'.join(lines))" \
|
|
102
|
+
> /pins/pyproject.toml
|
|
103
|
+
|
|
104
|
+
# ── Stage 3: Builder ─────────────────────────────────────────────────────────
|
|
105
|
+
FROM deps AS builder
|
|
106
|
+
|
|
107
|
+
# Everything SPLENT goes to its own prefix, pip's user site, rather than next
|
|
108
|
+
# to the third-party packages: the runtime stage copies each in its own layer.
|
|
109
|
+
# The product goes to another, since it changes with every commit.
|
|
110
|
+
ENV PYTHONUSERBASE=/opt/splent
|
|
111
|
+
ENV PYTHONPATH=/opt/product
|
|
112
|
+
ENV PATH=/opt/splent/bin:$PATH
|
|
88
113
|
|
|
89
|
-
# Install the features with pip, from PyPI. This is the ONLY place features are
|
|
90
|
-
# installed in a production image: scripts/00_install_features.sh (pip install -e
|
|
91
|
-
# against the local checkout) belongs to the dev entrypoint and never runs here.
|
|
92
|
-
# A feature declared without a version becomes a bare package name, so pip picks
|
|
93
|
-
# whatever PyPI serves rather than the code in the workspace.
|
|
94
114
|
ENV SPLENT_APP={{ product_name }}
|
|
95
115
|
ENV WORKING_DIR=/workspace
|
|
96
116
|
# The CLI's in-container detection keys on /.dockerenv, which the classic
|
|
@@ -112,11 +132,48 @@ ENV SPLENT_FORGE_HOST=${SPLENT_FORGE_HOST}
|
|
|
112
132
|
ENV SPLENT_FORGE_SSH_HOST=${SPLENT_FORGE_SSH_HOST}
|
|
113
133
|
ENV SPLENT_FORGE_SSH_PORT=${SPLENT_FORGE_SSH_PORT}
|
|
114
134
|
ENV SPLENT_FORGE_API=${SPLENT_FORGE_API}
|
|
115
|
-
|
|
135
|
+
|
|
136
|
+
COPY --from=pins /pins/runtime.txt /tmp/runtime.txt
|
|
137
|
+
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
138
|
+
pip install --user -r /tmp/runtime.txt
|
|
139
|
+
|
|
140
|
+
# Install the features with pip, from PyPI. This is the ONLY place features are
|
|
141
|
+
# installed in a production image: scripts/00_install_features.sh (pip install -e
|
|
142
|
+
# against the local checkout) belongs to the dev entrypoint and never runs here.
|
|
143
|
+
# A feature declared without a version becomes a bare package name, so pip picks
|
|
144
|
+
# whatever PyPI serves rather than the code in the workspace.
|
|
145
|
+
COPY --from=pins /pins/pyproject.toml {{ product_name }}/pyproject.toml
|
|
146
|
+
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
147
|
+
PIP_USER=1 splent feature:pip-install
|
|
148
|
+
|
|
149
|
+
# The product. Dockerfile.{{ product_name }}.prod.dockerignore keeps the env
|
|
150
|
+
# files (credentials), the development links and the deploy bundle out.
|
|
151
|
+
COPY . {{ product_name }}/
|
|
152
|
+
|
|
153
|
+
# Same protection for a builder that ignores that file (the classic builder,
|
|
154
|
+
# DOCKER_BUILDKIT=0). Only the runtime stage is pushed, and it copies
|
|
155
|
+
# /workspace from this stage, so the env files never reach a published image.
|
|
156
|
+
RUN find {{ product_name }}/docker -maxdepth 1 -name '.env*' ! -name '*.example' \
|
|
157
|
+
\( -type f -o -type l \) -delete
|
|
158
|
+
|
|
159
|
+
# The SPL's UVL model. The framework orders features, and their migrations,
|
|
160
|
+
# by its constraints and looks for it in .splent_cache/spls/. splent
|
|
161
|
+
# product:build keeps the model pyproject.toml pins in docker/spl/, and this
|
|
162
|
+
# puts it where the framework and the CLI look. Without it every boot warns
|
|
163
|
+
# that features load in pyproject.toml order.
|
|
164
|
+
RUN if [ -d {{ product_name }}/docker/spl ]; then \
|
|
165
|
+
mkdir -p .splent_cache/spls && \
|
|
166
|
+
cp -R {{ product_name }}/docker/spl/. .splent_cache/spls/; \
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# What it depends on is already installed, with the runtime.
|
|
170
|
+
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
171
|
+
pip install --no-deps --target /opt/product ./{{ product_name }}
|
|
116
172
|
|
|
117
173
|
# The product's own frontend, when its package.json has a build script,
|
|
118
174
|
# installed from its lock exactly as committed.
|
|
119
|
-
RUN
|
|
175
|
+
RUN --mount=type=cache,target=/root/.npm \
|
|
176
|
+
cd {{ product_name }} && \
|
|
120
177
|
if [ -f package.json ] && [ "$(npm pkg get scripts.build)" != "{}" ]; then \
|
|
121
178
|
test -f package-lock.json || \
|
|
122
179
|
{ echo "package-lock.json is missing: run npm install in the product and commit it"; exit 1; }; \
|
|
@@ -134,7 +191,7 @@ RUN splent feature:compile --verify
|
|
|
134
191
|
# The runtime stage copies /workspace, so they would ship otherwise.
|
|
135
192
|
RUN rm -rf {{ product_name }}/node_modules {{ product_name }}/build
|
|
136
193
|
|
|
137
|
-
# ── Stage
|
|
194
|
+
# ── Stage 4: Runtime ─────────────────────────────────────────────────────────
|
|
138
195
|
FROM python:3.13-slim-trixie
|
|
139
196
|
WORKDIR /workspace
|
|
140
197
|
|
|
@@ -150,6 +207,9 @@ ENV PIP_ROOT_USER_ACTION=ignore
|
|
|
150
207
|
ENV SPLENT_APP={{ product_name }}
|
|
151
208
|
ENV WORKING_DIR=/workspace
|
|
152
209
|
ENV SPLENT_CONTAINER=product
|
|
210
|
+
ENV PYTHONUSERBASE=/opt/splent
|
|
211
|
+
ENV PYTHONPATH=/opt/product
|
|
212
|
+
ENV PATH=/opt/splent/bin:$PATH
|
|
153
213
|
|
|
154
214
|
# The user the app runs as. The image keeps root as its user so the entrypoint
|
|
155
215
|
# can hand the named volumes to this one before it drops to it: see
|
|
@@ -160,9 +220,12 @@ RUN groupadd --system --gid 10001 splent \
|
|
|
160
220
|
&& mkdir -p /var/log/splent \
|
|
161
221
|
&& chown splent:splent /var/log/splent
|
|
162
222
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
COPY --from=
|
|
223
|
+
# The installed packages, one layer each: the third-party ones, then the
|
|
224
|
+
# framework, the CLI and the features, then the product.
|
|
225
|
+
COPY --from=deps /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
|
|
226
|
+
COPY --from=deps /usr/local/bin /usr/local/bin
|
|
227
|
+
COPY --from=builder /opt/splent /opt/splent
|
|
228
|
+
COPY --from=builder /opt/product /opt/product
|
|
166
229
|
|
|
167
230
|
# Copy product source (includes compiled assets, templates, migrations,
|
|
168
231
|
# entrypoints), owned by the app user, which writes its state file there.
|
|
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
|
|
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
|