splent-cli 1.47.0__tar.gz → 1.48.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.47.0/src/splent_cli.egg-info → splent_cli-1.48.1}/PKG-INFO +1 -1
- {splent_cli-1.47.0 → splent_cli-1.48.1}/pyproject.toml +1 -1
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_deps.py +40 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_release.py +83 -2
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_validate.py +15 -8
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/frontend.py +148 -6
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_.gitignore.j2 +3 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/Dockerfile.frontend +3 -1
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/package-lock.json +830 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/package.json +2 -0
- splent_cli-1.48.1/src/splent_cli/toolchain/frontend/splent/lint.js +147 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/splent/plugin.js +108 -9
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/splent/preset.js +41 -1
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/frontend_check.py +158 -11
- {splent_cli-1.47.0 → splent_cli-1.48.1/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli.egg-info/SOURCES.txt +1 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/COPYING +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/COPYING.LESSER +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/README.md +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/setup.cfg +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/check/check_vendor.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_compile.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_frontend_init.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_npm.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_test.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_build.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_deploy.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_frontend_audit.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_params.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_release.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/release/release_resume.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/tokens_setup.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/deploy_bundle.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/editable_refresh.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/image_registry.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/neutral_app.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/own_commands.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/preflight.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/pypi_quota.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/registry.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/release.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/release_dry_run.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/release_gate.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/spl_store.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/services/web_container.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/licenses/COPYING +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_.env.prod.example.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_400.html +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_401.html +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_404.html +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_500.html +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_app.css +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_app.js +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_base_template.html +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_dropzone.css +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_favicon.svg +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_own.css +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/splent/build.js +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/splent/legacy.js +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/toolchain/frontend/splent/singletons.json +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/ci_services.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/ci_workflow.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/db_client.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/deploy_freshness.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_naming.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_params.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_requirements.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_resolution.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/forge.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/frontend_licenses.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/menu_keys.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/migration_lock.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/npm_semver.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/redact.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/refinement_facts.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/running_version.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/toml_edit.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/upgrade_plan.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/utils/uvl_params.py +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli.egg-info/top_level.txt +0 -0
|
@@ -152,6 +152,34 @@ def _scan_feature_imports(
|
|
|
152
152
|
return python_imports, template_deps
|
|
153
153
|
|
|
154
154
|
|
|
155
|
+
def _optional_features(feature_path: str) -> set[str]:
|
|
156
|
+
"""The packages a feature's contract names in requires.features_optional.
|
|
157
|
+
|
|
158
|
+
A feature reaches those only when the product has them (a tolerant
|
|
159
|
+
import, a has_endpoint guard), so its reference to one needs no UVL
|
|
160
|
+
constraint: the model leaves the other feature optional, and nothing
|
|
161
|
+
breaks without it. Version ranges ("hubfile>=0.5.0") are dropped.
|
|
162
|
+
"""
|
|
163
|
+
import tomllib
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
with open(os.path.join(feature_path, "pyproject.toml"), "rb") as fh:
|
|
167
|
+
data = tomllib.load(fh)
|
|
168
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
169
|
+
return set()
|
|
170
|
+
requires = (
|
|
171
|
+
data.get("tool", {}).get("splent", {}).get("contract", {}).get("requires", {})
|
|
172
|
+
)
|
|
173
|
+
names = set()
|
|
174
|
+
for entry in requires.get("features_optional") or []:
|
|
175
|
+
name = re.split(r"[<>=!~\s]", str(entry), maxsplit=1)[0]
|
|
176
|
+
if name:
|
|
177
|
+
names.add(
|
|
178
|
+
name if name.startswith("splent_feature_") else f"splent_feature_{name}"
|
|
179
|
+
)
|
|
180
|
+
return names
|
|
181
|
+
|
|
182
|
+
|
|
155
183
|
# ---------------------------------------------------------------------------
|
|
156
184
|
# Feature path resolver
|
|
157
185
|
# ---------------------------------------------------------------------------
|
|
@@ -271,6 +299,7 @@ def check_deps():
|
|
|
271
299
|
|
|
272
300
|
py_imports, tpl_deps = _scan_feature_imports(fpath, pkg_name, all_packages)
|
|
273
301
|
allowed = allowed_deps.get(pkg_name, set())
|
|
302
|
+
optional = _optional_features(fpath)
|
|
274
303
|
all_deps = py_imports | tpl_deps
|
|
275
304
|
|
|
276
305
|
if not all_deps:
|
|
@@ -296,6 +325,17 @@ def check_deps():
|
|
|
296
325
|
)
|
|
297
326
|
)
|
|
298
327
|
ok += 1
|
|
328
|
+
elif imp in optional:
|
|
329
|
+
click.echo(
|
|
330
|
+
click.style(" [✔] ", fg="green")
|
|
331
|
+
+ click.style(f"{short}", bold=True)
|
|
332
|
+
+ f" {source} {imp_short}"
|
|
333
|
+
+ click.style(
|
|
334
|
+
f" (optional: {short}'s contract lists it in features_optional)",
|
|
335
|
+
fg="bright_black",
|
|
336
|
+
)
|
|
337
|
+
)
|
|
338
|
+
ok += 1
|
|
299
339
|
else:
|
|
300
340
|
reverse_allowed = allowed_deps.get(imp, set())
|
|
301
341
|
if pkg_name in reverse_allowed:
|
|
@@ -237,6 +237,68 @@ def _extract_templates(src_dir: Path) -> list[str]:
|
|
|
237
237
|
|
|
238
238
|
|
|
239
239
|
_SLOT_CALL = re.compile(r"""(?:get|render)_template_hooks\s*\(\s*['"]([^'"]+)['"]""")
|
|
240
|
+
_SLOT_FUNCTIONS = ("get_template_hooks", "render_template_hooks")
|
|
241
|
+
_MACRO = re.compile(
|
|
242
|
+
r"\{%-?\s*macro\s+(\w+)\s*\(\s*(\w+)(.*?)\{%-?\s*endmacro", re.DOTALL
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _python_slot_helpers(source: str) -> set[str]:
|
|
247
|
+
"""The functions of SOURCE that open the slot their first argument
|
|
248
|
+
names: they hand it to get_template_hooks or render_template_hooks."""
|
|
249
|
+
try:
|
|
250
|
+
tree = ast.parse(source)
|
|
251
|
+
except SyntaxError:
|
|
252
|
+
return set()
|
|
253
|
+
helpers = set()
|
|
254
|
+
for node in ast.walk(tree):
|
|
255
|
+
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
256
|
+
continue
|
|
257
|
+
params = [arg.arg for arg in node.args.posonlyargs + node.args.args]
|
|
258
|
+
if params[:1] in (["self"], ["cls"]):
|
|
259
|
+
params = params[1:]
|
|
260
|
+
if not params:
|
|
261
|
+
continue
|
|
262
|
+
for call in ast.walk(node):
|
|
263
|
+
if not isinstance(call, ast.Call) or not call.args:
|
|
264
|
+
continue
|
|
265
|
+
called = call.func
|
|
266
|
+
if isinstance(called, ast.Attribute):
|
|
267
|
+
name = called.attr
|
|
268
|
+
elif isinstance(called, ast.Name):
|
|
269
|
+
name = called.id
|
|
270
|
+
else:
|
|
271
|
+
name = None
|
|
272
|
+
first = call.args[0]
|
|
273
|
+
if (
|
|
274
|
+
name in _SLOT_FUNCTIONS
|
|
275
|
+
and isinstance(first, ast.Name)
|
|
276
|
+
and first.id == params[0]
|
|
277
|
+
):
|
|
278
|
+
helpers.add(node.name)
|
|
279
|
+
break
|
|
280
|
+
return helpers
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _template_slot_helpers(source: str) -> set[str]:
|
|
284
|
+
"""The macros of SOURCE that open the slot their first argument names."""
|
|
285
|
+
helpers = set()
|
|
286
|
+
for name, param, body in _MACRO.findall(source):
|
|
287
|
+
opens = re.compile(
|
|
288
|
+
r"(?:get|render)_template_hooks\s*\(\s*" + re.escape(param) + r"\s*[,)]"
|
|
289
|
+
)
|
|
290
|
+
if opens.search(body):
|
|
291
|
+
helpers.add(name)
|
|
292
|
+
return helpers
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _helper_calls(helpers: set[str]) -> re.Pattern | None:
|
|
296
|
+
"""A call of one of HELPERS with a slot name as its first argument, as
|
|
297
|
+
a template writes it: ``hub.slot_of("x.y", …)`` or ``slot_of('x.y')``."""
|
|
298
|
+
if not helpers:
|
|
299
|
+
return None
|
|
300
|
+
names = "|".join(re.escape(name) for name in sorted(helpers))
|
|
301
|
+
return re.compile(r"""\b(?:""" + names + r""")\s*\(\s*['"]([^'"]+)['"]""")
|
|
240
302
|
|
|
241
303
|
|
|
242
304
|
def _extract_template_hook_slots(src_dir: Path) -> list[str]:
|
|
@@ -250,19 +312,38 @@ def _extract_template_hook_slots(src_dir: Path) -> list[str]:
|
|
|
250
312
|
that slot is as much the feature's: a scan of the templates alone left
|
|
251
313
|
it out of ``extensible.hooks``. Tests are not the feature's code, and a
|
|
252
314
|
layout.* slot the code reads is the product's.
|
|
315
|
+
|
|
316
|
+
A helper may also take the slot's name from its caller: a function of
|
|
317
|
+
the feature's Python, or a macro of its templates, that hands its first
|
|
318
|
+
argument to one of the two. Then the slot is the literal a template
|
|
319
|
+
(or the code) passes it, ``shell.slot_of("layout.sidebar", …)``.
|
|
253
320
|
"""
|
|
254
321
|
if not src_dir.exists():
|
|
255
322
|
return []
|
|
256
323
|
slots: set[str] = set()
|
|
257
324
|
templates_dir = src_dir / "templates"
|
|
325
|
+
templates = {}
|
|
258
326
|
if templates_dir.exists():
|
|
259
327
|
for html_file in templates_dir.rglob("*.html"):
|
|
260
|
-
|
|
328
|
+
templates[html_file] = html_file.read_text()
|
|
329
|
+
sources = {}
|
|
261
330
|
for py_file in _feature_sources(src_dir):
|
|
262
331
|
try:
|
|
263
|
-
|
|
332
|
+
sources[py_file] = py_file.read_text()
|
|
264
333
|
except (OSError, UnicodeDecodeError):
|
|
265
334
|
continue
|
|
335
|
+
helpers = set()
|
|
336
|
+
for text in sources.values():
|
|
337
|
+
helpers |= _python_slot_helpers(text)
|
|
338
|
+
for text in templates.values():
|
|
339
|
+
helpers |= _template_slot_helpers(text)
|
|
340
|
+
calls = _helper_calls(helpers)
|
|
341
|
+
for text in templates.values():
|
|
342
|
+
slots.update(_SLOT_CALL.findall(text))
|
|
343
|
+
if calls:
|
|
344
|
+
slots.update(calls.findall(text))
|
|
345
|
+
for text in sources.values():
|
|
346
|
+
found = _SLOT_CALL.findall(text) + (calls.findall(text) if calls else [])
|
|
266
347
|
slots.update(s for s in found if not s.startswith(_PRODUCT_SLOT_PREFIX))
|
|
267
348
|
return sorted(slots)
|
|
268
349
|
|
|
@@ -19,8 +19,9 @@ product:validate checks a product in seven phases.
|
|
|
19
19
|
7. Frontend (bundles). Does the shell provide, at a version in range,
|
|
20
20
|
what each feature with frontend/ takes from the page? Does each bundle
|
|
21
21
|
match its manifest, ship what its licence allows and no singleton
|
|
22
|
-
another one ships
|
|
23
|
-
|
|
22
|
+
another one ships, and load nothing from another origin? Does the
|
|
23
|
+
product approve what each one asks to add to the CSP? What legacy
|
|
24
|
+
features use and load is said (splent_cli.utils.frontend_check).
|
|
24
25
|
"""
|
|
25
26
|
|
|
26
27
|
import os
|
|
@@ -206,6 +207,7 @@ def _run_deps_check(workspace, product, data=None, uvl_path=None):
|
|
|
206
207
|
a model to check against instead (--model).
|
|
207
208
|
"""
|
|
208
209
|
from splent_cli.commands.check.check_deps import (
|
|
210
|
+
_optional_features,
|
|
209
211
|
_parse_uvl_deps,
|
|
210
212
|
_resolve_feature_paths,
|
|
211
213
|
_scan_feature_imports,
|
|
@@ -258,6 +260,8 @@ def _run_deps_check(workspace, product, data=None, uvl_path=None):
|
|
|
258
260
|
|
|
259
261
|
py_imports, tpl_deps = _scan_feature_imports(fpath, pkg_name, all_packages)
|
|
260
262
|
allowed = allowed_deps.get(pkg_name, set())
|
|
263
|
+
# What the feature reaches only when the product has it.
|
|
264
|
+
optional = _optional_features(fpath)
|
|
261
265
|
all_deps = py_imports | tpl_deps
|
|
262
266
|
|
|
263
267
|
if not all_deps:
|
|
@@ -266,7 +270,7 @@ def _run_deps_check(workspace, product, data=None, uvl_path=None):
|
|
|
266
270
|
|
|
267
271
|
for imp in sorted(all_deps):
|
|
268
272
|
imp_short = pkg_to_short.get(imp, imp)
|
|
269
|
-
if imp in allowed:
|
|
273
|
+
if imp in allowed or imp in optional:
|
|
270
274
|
ok_count += 1
|
|
271
275
|
else:
|
|
272
276
|
reverse_allowed = allowed_deps.get(imp, set())
|
|
@@ -610,7 +614,8 @@ def _run_frontend_check(workspace, product_dir, data, env=None):
|
|
|
610
614
|
from splent_cli.utils.frontend_check import check_product
|
|
611
615
|
|
|
612
616
|
entries = read_features_from_data(data, env or os.getenv("SPLENT_ENV", "dev"))
|
|
613
|
-
|
|
617
|
+
approvals = ((data.get("tool") or {}).get("splent") or {}).get("csp")
|
|
618
|
+
return check_product(workspace, product_dir, entries, csp_approvals=approvals)
|
|
614
619
|
|
|
615
620
|
|
|
616
621
|
def _report_frontend(check) -> bool:
|
|
@@ -619,10 +624,12 @@ def _report_frontend(check) -> bool:
|
|
|
619
624
|
What the shell provides comes first, and where it was read. A peer or
|
|
620
625
|
capability nothing provides, or at a version out of its range, a
|
|
621
626
|
singleton two bundles ship, a licence the feature cannot ship, a
|
|
622
|
-
release whose bundle is not the one it built
|
|
623
|
-
takes without declaring it
|
|
624
|
-
|
|
625
|
-
|
|
627
|
+
release whose bundle is not the one it built, a global a template
|
|
628
|
+
takes without declaring it, a feature with frontend/ that loads from
|
|
629
|
+
another origin and a CSP source the policy never takes are errors. A
|
|
630
|
+
legacy feature, a working copy built before its sources changed, what
|
|
631
|
+
a legacy feature or the product loads from another origin and a CSP
|
|
632
|
+
request the product does not approve are warnings.
|
|
626
633
|
"""
|
|
627
634
|
from splent_cli.utils.frontend_check import FRONTEND
|
|
628
635
|
|
|
@@ -12,8 +12,11 @@ legacy: its ``assets/js/webpack.config.js`` builds against the product's
|
|
|
12
12
|
node_modules in the product's web container, exactly as before, until CLI
|
|
13
13
|
2.0.
|
|
14
14
|
|
|
15
|
-
A build
|
|
16
|
-
|
|
15
|
+
A build lints the feature's own code first, with eslint's ``no-undef``
|
|
16
|
+
against the globals of the page it declares (toolchain/frontend/splent/
|
|
17
|
+
lint.js), and fails on a name it never declared. It writes ``assets/dist``
|
|
18
|
+
and ``assets/meta/manifest.json``, the files it wrote, the packages it put
|
|
19
|
+
in them and the page globals its code reads. A production build adds
|
|
17
20
|
``assets/meta/third_party.json``, their licence texts, and a release
|
|
18
21
|
(:func:`release_build`) adds ``assets/meta/sbom.cdx.json`` and gates on
|
|
19
22
|
the licences (utils/frontend_licenses.py) and on ``npm audit``.
|
|
@@ -31,9 +34,12 @@ import datetime
|
|
|
31
34
|
import hashlib
|
|
32
35
|
import json
|
|
33
36
|
import os
|
|
37
|
+
import re
|
|
34
38
|
import shutil
|
|
35
39
|
import socket
|
|
36
40
|
import subprocess
|
|
41
|
+
import sys
|
|
42
|
+
import tempfile
|
|
37
43
|
import uuid
|
|
38
44
|
from pathlib import Path
|
|
39
45
|
|
|
@@ -408,14 +414,20 @@ def npm(
|
|
|
408
414
|
|
|
409
415
|
|
|
410
416
|
def install(feature_root, *, image=None, force=False, echo=click.echo) -> None:
|
|
411
|
-
"""Install
|
|
417
|
+
"""Install what the build takes from the network: frontend/node_modules
|
|
418
|
+
from the lock, and the files splent.vendored fetches
|
|
419
|
+
(:func:`fetch_vendored`), each unless it already is. FORCE installs
|
|
420
|
+
anyway, as a release does."""
|
|
421
|
+
_install_node_modules(feature_root, image=image, force=force, echo=echo)
|
|
422
|
+
fetch_vendored(feature_root, force=force, echo=echo)
|
|
412
423
|
|
|
413
|
-
|
|
424
|
+
|
|
425
|
+
def _install_node_modules(feature_root, *, image, force, echo) -> None:
|
|
426
|
+
"""``npm ci --omit=peer --ignore-scripts``: exactly the lock, no peer (the
|
|
414
427
|
shell provides those, so a peer the bundle forgot to declare fails as
|
|
415
428
|
"Module not found" instead of shipping a second copy) and no install
|
|
416
429
|
script. The sha256 of the lock it installed is sealed into node_modules,
|
|
417
|
-
so the next build installs only when the lock changed.
|
|
418
|
-
anyway, as a release does.
|
|
430
|
+
so the next build installs only when the lock changed.
|
|
419
431
|
"""
|
|
420
432
|
frontend = Path(feature_root) / FRONTEND_DIR
|
|
421
433
|
lock = frontend / LOCK
|
|
@@ -452,6 +464,136 @@ def install(feature_root, *, image=None, force=False, echo=click.echo) -> None:
|
|
|
452
464
|
_give_back(seal)
|
|
453
465
|
|
|
454
466
|
|
|
467
|
+
# ── Vendored files ────────────────────────────────────────────────────
|
|
468
|
+
|
|
469
|
+
# What a feature serves that is not npm's (the Python wheels an in-browser
|
|
470
|
+
# runtime installs), as splent.vendored declares it: {name, version,
|
|
471
|
+
# license, source, path, sha256}, ``path`` being where it is served, below
|
|
472
|
+
# assets/. One served from assets/dist is copied there by the build, from
|
|
473
|
+
# the feature's repository when it names ``from`` (a file of frontend/,
|
|
474
|
+
# such as a wheel the feature builds itself), or else from what this step
|
|
475
|
+
# fetched into frontend/vendored/, which no repository keeps.
|
|
476
|
+
VENDORED_DIR = "vendored"
|
|
477
|
+
|
|
478
|
+
# name-version(-build)?-python-abi-platform.whl
|
|
479
|
+
_WHEEL = re.compile(
|
|
480
|
+
r"^(?P<name>[^-]+)-(?P<version>[^-]+)(?:-\d[^-]*)?"
|
|
481
|
+
r"-(?P<python>[^-]+)-(?P<abi>[^-]+)-(?P<platform>[^-]+)\.whl$"
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def vendored(pkg: dict) -> list[dict]:
|
|
486
|
+
"""splent.vendored of PKG, the entries it declares as dicts."""
|
|
487
|
+
entries = (pkg.get("splent") or {}).get("vendored") or []
|
|
488
|
+
return [entry for entry in entries if isinstance(entry, dict)]
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _pip_download_args(entry: dict, filename: str, dest: Path) -> list[str]:
|
|
492
|
+
"""``pip download`` of exactly FILENAME: a wheel, from PyPI when the
|
|
493
|
+
entry's source is ``pypi``, or from its https URL, for the platform its
|
|
494
|
+
tags name (``any`` for a pure one, ``pyodide_2024_0_wasm32`` for one
|
|
495
|
+
built for Pyodide)."""
|
|
496
|
+
name = entry.get("name") or "?"
|
|
497
|
+
tags = _WHEEL.match(filename)
|
|
498
|
+
if tags is None:
|
|
499
|
+
raise FrontendError(
|
|
500
|
+
f"splent.vendored {name}: {filename} is no wheel, and only wheels are "
|
|
501
|
+
"fetched. Keep the file in frontend/ and name it in from."
|
|
502
|
+
)
|
|
503
|
+
source = str(entry.get("source") or "pypi")
|
|
504
|
+
if source == "pypi":
|
|
505
|
+
requirement = f"{entry.get('name')}=={entry.get('version')}"
|
|
506
|
+
elif source.startswith("https://"):
|
|
507
|
+
requirement = source
|
|
508
|
+
else:
|
|
509
|
+
raise FrontendError(
|
|
510
|
+
f"splent.vendored {name}: source is pypi or the https URL of the "
|
|
511
|
+
f"wheel, not {source!r}; a file the feature keeps names it in from."
|
|
512
|
+
)
|
|
513
|
+
args = [
|
|
514
|
+
sys.executable,
|
|
515
|
+
"-m",
|
|
516
|
+
"pip",
|
|
517
|
+
"download",
|
|
518
|
+
"--no-deps",
|
|
519
|
+
"--quiet",
|
|
520
|
+
"--disable-pip-version-check",
|
|
521
|
+
"--only-binary",
|
|
522
|
+
":all:",
|
|
523
|
+
"--dest",
|
|
524
|
+
str(dest),
|
|
525
|
+
"--platform",
|
|
526
|
+
tags["platform"],
|
|
527
|
+
]
|
|
528
|
+
python = tags["python"].split(".")[0]
|
|
529
|
+
if python.startswith("cp") and python[2:].isdigit():
|
|
530
|
+
version = f"{python[2]}.{python[3:]}"
|
|
531
|
+
args += ["--implementation", "cp", "--python-version", version]
|
|
532
|
+
args += ["--abi", tags["abi"].split(".")[0]]
|
|
533
|
+
return [*args, requirement]
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def fetch_vendored(feature_root, *, force=False, echo=click.echo) -> None:
|
|
537
|
+
"""Fetch the wheels splent.vendored serves from assets/dist into
|
|
538
|
+
frontend/vendored/, with ``pip download``, and check each against the
|
|
539
|
+
sha256 the entry declares; and check the files the feature keeps for
|
|
540
|
+
it (``from``). A file already there with its sha256 is not fetched
|
|
541
|
+
again, unless FORCE, as a release does."""
|
|
542
|
+
if not declares(feature_root):
|
|
543
|
+
return
|
|
544
|
+
entries = vendored(package(feature_root))
|
|
545
|
+
frontend = Path(feature_root) / FRONTEND_DIR
|
|
546
|
+
target = frontend / VENDORED_DIR
|
|
547
|
+
for entry in entries:
|
|
548
|
+
name = entry.get("name") or "?"
|
|
549
|
+
expected = entry.get("sha256")
|
|
550
|
+
if not str(entry.get("path") or "").startswith("dist/"):
|
|
551
|
+
continue
|
|
552
|
+
if not expected:
|
|
553
|
+
raise FrontendError(f"splent.vendored {name} needs a sha256")
|
|
554
|
+
if entry.get("from"):
|
|
555
|
+
kept = (frontend / str(entry["from"])).resolve()
|
|
556
|
+
if not kept.is_relative_to(frontend.resolve()) or not kept.is_file():
|
|
557
|
+
raise FrontendError(
|
|
558
|
+
f"splent.vendored {name}: from names {entry['from']}, which is "
|
|
559
|
+
"not a file in frontend/"
|
|
560
|
+
)
|
|
561
|
+
if _sha256(kept) != expected:
|
|
562
|
+
raise FrontendError(
|
|
563
|
+
f"splent.vendored {name}: frontend/{entry['from']} is not the "
|
|
564
|
+
"file its sha256 names"
|
|
565
|
+
)
|
|
566
|
+
continue
|
|
567
|
+
filename = Path(str(entry["path"])).name
|
|
568
|
+
path = target / filename
|
|
569
|
+
if not force and path.is_file() and _sha256(path) == expected:
|
|
570
|
+
continue
|
|
571
|
+
echo(f" pip fetching {filename} (splent.vendored)...")
|
|
572
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
573
|
+
with tempfile.TemporaryDirectory(dir=target) as scratch:
|
|
574
|
+
args = _pip_download_args(entry, filename, Path(scratch))
|
|
575
|
+
result = subprocess.run(
|
|
576
|
+
args, capture_output=True, text=True, timeout=INSTALL_TIMEOUT
|
|
577
|
+
)
|
|
578
|
+
got = Path(scratch) / filename
|
|
579
|
+
if result.returncode != 0 or not got.is_file():
|
|
580
|
+
said = _head(result.stderr or result.stdout, 4) or " (no output)"
|
|
581
|
+
found = ", ".join(sorted(p.name for p in Path(scratch).iterdir()))
|
|
582
|
+
raise FrontendError(
|
|
583
|
+
f"pip download did not fetch {filename}"
|
|
584
|
+
+ (f" (it fetched {found})" if found else "")
|
|
585
|
+
+ f":\n{said}"
|
|
586
|
+
)
|
|
587
|
+
if _sha256(got) != expected:
|
|
588
|
+
raise FrontendError(
|
|
589
|
+
f"splent.vendored {name}: the {filename} pip fetched has another "
|
|
590
|
+
"sha256 than the one the entry declares"
|
|
591
|
+
)
|
|
592
|
+
got.replace(path)
|
|
593
|
+
_give_back(target)
|
|
594
|
+
_give_back(path)
|
|
595
|
+
|
|
596
|
+
|
|
455
597
|
def _give_back(path: Path) -> None:
|
|
456
598
|
"""A file this process wrote as root goes to the workspace's owner."""
|
|
457
599
|
if os.geteuid() != 0:
|
{splent_cli-1.47.0 → splent_cli-1.48.1}/src/splent_cli/templates/feature/feature_.gitignore.j2
RENAMED
|
@@ -26,6 +26,9 @@ app.log.*
|
|
|
26
26
|
/dist/
|
|
27
27
|
src/*/*/assets/dist/
|
|
28
28
|
src/*/*/assets/meta/
|
|
29
|
+
# The wheels splent.vendored names, which feature:compile and the release
|
|
30
|
+
# fetch into frontend/vendored/ and check against their sha256.
|
|
31
|
+
frontend/vendored/
|
|
29
32
|
{% else %}# Build artifacts: the package build's, at the root, and the bundles and
|
|
30
33
|
# source maps webpack writes to assets/dist. Only those: the dist/ of a
|
|
31
34
|
# vendored npm package is its source here, webpack resolves the package
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
#
|
|
3
3
|
# A feature declares its JavaScript dependencies in frontend/package.json,
|
|
4
4
|
# and this image brings everything else: Node, npm, webpack, the loaders and
|
|
5
|
-
# plugins,
|
|
5
|
+
# plugins, eslint (every build lints the feature's own code for globals of
|
|
6
|
+
# the page it never declared), the SPLENT preset and the registry of
|
|
7
|
+
# singletons. webpack is no
|
|
6
8
|
# devDependency of any feature, so a feature's node_modules holds what its
|
|
7
9
|
# bundle ships and nothing more, and every feature builds with the same
|
|
8
10
|
# toolchain, the way a Python package builds in an isolated [build-system].
|