splent-cli 1.46.0__tar.gz → 1.48.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.46.0/src/splent_cli.egg-info → splent_cli-1.48.0}/PKG-INFO +1 -1
- {splent_cli-1.46.0 → splent_cli-1.48.0}/pyproject.toml +4 -2
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_compile.py +136 -12
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_release.py +83 -2
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_build.py +36 -1
- splent_cli-1.48.0/src/splent_cli/commands/product/product_frontend_audit.py +323 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_release.py +2 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_validate.py +108 -7
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/frontend.py +224 -28
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/preflight.py +30 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_.gitignore.j2 +3 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_Dockerfile.prod.j2 +21 -12
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_ci_validate.yml.j2 +21 -4
- splent_cli-1.48.0/src/splent_cli/toolchain/Dockerfile.frontend +48 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/package-lock.json +2130 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/package.json +16 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/build.js +76 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/legacy.js +52 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/lint.js +147 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/plugin.js +387 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/preset.js +248 -0
- splent_cli-1.48.0/src/splent_cli/toolchain/frontend/splent/singletons.json +18 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/deploy_freshness.py +8 -3
- splent_cli-1.48.0/src/splent_cli/utils/frontend_check.py +1013 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/frontend_licenses.py +4 -2
- splent_cli-1.48.0/src/splent_cli/utils/npm_semver.py +200 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0/src/splent_cli.egg-info}/PKG-INFO +1 -1
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli.egg-info/SOURCES.txt +12 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/COPYING +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/COPYING.LESSER +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/README.md +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/setup.cfg +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/__main__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/cli.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_clear.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_orphans.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_outdated.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_prune.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_size.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_status.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_usage.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/cache/cache_versions.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_contracts.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_deps.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_docker.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_env.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_features.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_github.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_infra.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_product.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_pypi.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_pyproject.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_releases.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/check/check_vendor.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/clear/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/clear/clear_build.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/clear/clear_log.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/clear/clear_uploads.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/command/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/command/command_create.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/coverage.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_console.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_dump.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_migrate.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_reset.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_restore.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_rollback.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_seed.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_status.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/database/db_upgrade.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/doctor.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/env/env_list.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/env/env_set.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/env/env_show.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/export/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/export/export_puml.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_add.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_attach.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_clean.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_clone.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_compat.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_contract.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_create.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_delete.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_detach.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_discard.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_drift.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_env.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_fork.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_frontend_init.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_git.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_hook_add.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_hook_remove.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_hooks.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_impact.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_info.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_inject_config.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_install.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_list.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_npm.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_order.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_outdated.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_pin.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_pip_install.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_pull.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_refine.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_remove.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_rename.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_search.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_status.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_sync_template.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_test.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_translate.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_unlock.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_upgrade.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_versions.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/feature/feature_xray.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/lint.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/locust.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/marketplace/login.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/marketplace/logout.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/marketplace/marketplace_auth_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/marketplace/marketplace_index.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/marketplace/whoami.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_auto_require.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_clean.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_commands.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_config.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_configure.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_console.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_containers.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_create.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_deploy.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_derive.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_deselect.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_down.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_drift.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_env.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_list.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_logs.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_missing.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_params.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_port.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_resolve.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_restart.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_routes.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_run.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_select.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_shell.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_signals.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_sync_template.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_test.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/product/product_up.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/release/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/release/release_core.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/release/release_resume.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/selenium.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_add_constraints.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_add_feature.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_compare.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_configurations.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_create.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_deps.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_features.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_fetch.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_info.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_list.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_outdated.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_pin.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_publish.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_remove_feature.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/spl/spl_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/tokens_setup.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/uvl/uvl_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/commands/version.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/compose.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/context.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/credentials.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/deploy_bundle.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/editable_refresh.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/image_registry.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/index_refresh.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/marketplace.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/marketplace_api.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/marketplace_url.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/neutral_app.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/own_commands.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/pypi_quota.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/registry.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/release.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/release_dry_run.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/release_gate.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/spl_store.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/services/web_container.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_MANIFEST.in.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_README.md.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_commands.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_config.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_docker.env.example.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_forms.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_frontend.npmrc.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_frontend_scripts.js.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_hooks.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_init.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_init_config.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_init_light.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_init_service.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_migrations_alembic.ini.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_migrations_env.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_migrations_script.py.mako.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_models.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_pyproject.toml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_pyproject_minimal.toml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_repositories.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_routes.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_scripts.js.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_seeders.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_services.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_services_service.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_signals.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_templates_index.html.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_conftest.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_e2e.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_functional.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_integration.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_locustfile.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_tests_unit.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/feature/feature_webpack.config.js.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/licenses/COPYING +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/licenses/COPYING.LESSER +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_.env.dev.example.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_.env.prod.example.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_.gitignore.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_00_core_requirements_dev.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_00_install_features.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_01_compile_assets.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_02_0_db_wait_connection.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_02_1_db_create_db_test.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_02_2_db_create_splent_migrations.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_03_initialize_migrations.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_04_handle_migrations.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_05_0_start_app_dev.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_05_1_start_app_prod.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_400.html +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_401.html +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_404.html +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_500.html +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_Dockerfile.dev.dockerignore.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_Dockerfile.dev.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_Dockerfile.prod.dockerignore.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_README.md.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_app.css +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_app.js +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_base_template.html +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_ci_upgrade.yml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_config.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_docker-compose.dev.yml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_docker-compose.prod.yml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_dropzone.css +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_entrypoint.dev.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_entrypoint.prod.sh.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_errors.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_favicon.svg +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_init.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_logging.py.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_own.css +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_package.json.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_pyproject.toml.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/templates/product/product_uvl.j2 +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/__init__.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/archetype.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/cache_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/ci_services.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/ci_workflow.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/command_loader.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/contract_freshness.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/db_client.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/db_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/decorators.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/dynamic_imports.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_installer.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_naming.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_params.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_requirements.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_resolution.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/feature_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/forge.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/git_url.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/integrity.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/io_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/lifecycle.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/manifest.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/menu_keys.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/migration_lock.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/path_utils.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/proc.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/redact.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/refinement_facts.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/running_version.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/template_drift.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/toml_edit.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/upgrade_plan.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/uvl_diagnosis.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli/utils/uvl_params.py +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli.egg-info/dependency_links.txt +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli.egg-info/entry_points.txt +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli.egg-info/requires.txt +0 -0
- {splent_cli-1.46.0 → splent_cli-1.48.0}/src/splent_cli.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "splent_cli"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.48.0"
|
|
8
8
|
description = "SPLENT-CLI is a CLI to be able to work on your development more easily."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13"
|
|
@@ -78,8 +78,10 @@ where = ["src"]
|
|
|
78
78
|
|
|
79
79
|
# The templates product:create, feature:create and the rest render. They are
|
|
80
80
|
# data, not modules, so without this a wheel from PyPI shipped none of them.
|
|
81
|
+
# The build context of the frontend toolchain image too, so a CLI installed
|
|
82
|
+
# from PyPI can build the image a feature with frontend/ builds in.
|
|
81
83
|
[tool.setuptools.package-data]
|
|
82
|
-
splent_cli = ["templates/**/*"]
|
|
84
|
+
splent_cli = ["templates/**/*", "toolchain/**/*"]
|
|
83
85
|
|
|
84
86
|
[tool.pytest.ini_options]
|
|
85
87
|
testpaths = ["tests"]
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import dataclasses
|
|
1
2
|
import logging
|
|
2
3
|
import os
|
|
3
4
|
import posixpath
|
|
4
5
|
import shlex
|
|
5
6
|
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
6
8
|
|
|
7
9
|
import click
|
|
8
10
|
|
|
@@ -368,20 +370,43 @@ def _compile_frontend(root, feature, *, watch, production, release_build) -> boo
|
|
|
368
370
|
|
|
369
371
|
|
|
370
372
|
def _verify_features(features, workspace: str) -> int:
|
|
371
|
-
"""feature:compile --verify: 1 when the
|
|
372
|
-
frontend/ does not match its
|
|
373
|
-
|
|
373
|
+
"""feature:compile --verify: 1 when the bundle of any feature with
|
|
374
|
+
frontend/ does not match its manifest, else 0.
|
|
375
|
+
|
|
376
|
+
A working copy is checked at the workspace root, a pinned feature in the
|
|
377
|
+
copy the product links, which is what its app serves, and where neither
|
|
378
|
+
is, as in the production image, in the package pip installed. When the
|
|
379
|
+
product has docker/frontend.lock.json (product:build writes it), every
|
|
380
|
+
feature it lists is checked too, and has to carry the manifest the lock
|
|
381
|
+
names, of the version it names: the bundle its release built.
|
|
382
|
+
"""
|
|
383
|
+
from splent_cli.utils.frontend_check import read_lock
|
|
384
|
+
|
|
385
|
+
product = context.active_app(workspace)
|
|
386
|
+
lock = read_lock(os.path.join(workspace, product)) if product else None
|
|
387
|
+
locked = (lock or {}).get("features") or {}
|
|
388
|
+
refs = list(features)
|
|
389
|
+
named = {_split_ref(feature)[1] for feature in refs}
|
|
390
|
+
for name, entry in sorted(locked.items()):
|
|
391
|
+
if name not in named:
|
|
392
|
+
version = entry.get("version")
|
|
393
|
+
namespace = entry.get("namespace") or "splent_io"
|
|
394
|
+
refs.append(f"{namespace}/{name}" + (f"@v{version}" if version else ""))
|
|
395
|
+
|
|
374
396
|
failed = 0
|
|
375
|
-
for feature in
|
|
397
|
+
for feature in refs:
|
|
376
398
|
namespace, base_name, version = _split_ref(feature)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
399
|
+
entry = locked.get(base_name)
|
|
400
|
+
copy = _copy_of(workspace, product, namespace, base_name, version)
|
|
401
|
+
if copy is None and entry is not None:
|
|
402
|
+
failed += 1
|
|
403
|
+
click.secho(
|
|
404
|
+
f"❌ {feature}: docker/frontend.lock.json lists it, and it is "
|
|
405
|
+
"neither here nor installed",
|
|
406
|
+
fg="red",
|
|
381
407
|
)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if not frontend.declares(root):
|
|
408
|
+
continue
|
|
409
|
+
if entry is None and (copy is None or not copy.declares):
|
|
385
410
|
click.echo(
|
|
386
411
|
click.style(
|
|
387
412
|
f" {feature} has no frontend/, nothing to verify.",
|
|
@@ -389,7 +414,11 @@ def _verify_features(features, workspace: str) -> int:
|
|
|
389
414
|
)
|
|
390
415
|
)
|
|
391
416
|
continue
|
|
392
|
-
problems = frontend.
|
|
417
|
+
problems = frontend.verify_assets(
|
|
418
|
+
copy.assets, lock=copy.lock, production=bool(version) or copy.installed
|
|
419
|
+
)
|
|
420
|
+
if entry is not None:
|
|
421
|
+
problems += _against_lock(copy, entry, base_name)
|
|
393
422
|
if problems:
|
|
394
423
|
failed += 1
|
|
395
424
|
click.secho(
|
|
@@ -402,6 +431,101 @@ def _verify_features(features, workspace: str) -> int:
|
|
|
402
431
|
return 1 if failed else 0
|
|
403
432
|
|
|
404
433
|
|
|
434
|
+
@dataclasses.dataclass
|
|
435
|
+
class _Copy:
|
|
436
|
+
"""The copy of a feature --verify checks: its assets/, its frontend
|
|
437
|
+
lock (a package pip installed has none), whether pip installed it, and
|
|
438
|
+
whether it declares frontend/ (an installed one does when it carries a
|
|
439
|
+
manifest)."""
|
|
440
|
+
|
|
441
|
+
assets: Path
|
|
442
|
+
lock: Path | None
|
|
443
|
+
installed: bool
|
|
444
|
+
declares: bool
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _copy_of(workspace, product, namespace, base_name, version) -> _Copy | None:
|
|
448
|
+
"""The working copy, the product's link to a pinned release, or else
|
|
449
|
+
the package pip installed. None when there is none."""
|
|
450
|
+
if version:
|
|
451
|
+
root = (
|
|
452
|
+
os.path.join(
|
|
453
|
+
workspace, product, "features", namespace, f"{base_name}@{version}"
|
|
454
|
+
)
|
|
455
|
+
if product
|
|
456
|
+
else None
|
|
457
|
+
)
|
|
458
|
+
else:
|
|
459
|
+
root = os.path.join(workspace, base_name)
|
|
460
|
+
if root and os.path.isdir(root):
|
|
461
|
+
return _Copy(
|
|
462
|
+
frontend.assets_dir(root, namespace, base_name),
|
|
463
|
+
Path(root) / frontend.FRONTEND_DIR / frontend.LOCK,
|
|
464
|
+
False,
|
|
465
|
+
frontend.declares(root),
|
|
466
|
+
)
|
|
467
|
+
import importlib.util
|
|
468
|
+
|
|
469
|
+
try:
|
|
470
|
+
spec = importlib.util.find_spec(f"{namespace}.{base_name}")
|
|
471
|
+
except (ImportError, ValueError):
|
|
472
|
+
spec = None
|
|
473
|
+
if spec is None or not spec.submodule_search_locations:
|
|
474
|
+
return None
|
|
475
|
+
assets = Path(list(spec.submodule_search_locations)[0]) / "assets"
|
|
476
|
+
return _Copy(assets, None, True, (assets / "meta" / "manifest.json").is_file())
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _against_lock(copy: _Copy, entry: dict, base_name: str) -> list[str]:
|
|
480
|
+
"""What does not match between a feature's bundle and its entry in
|
|
481
|
+
docker/frontend.lock.json."""
|
|
482
|
+
problems = []
|
|
483
|
+
wanted = entry.get("manifest_sha256")
|
|
484
|
+
found = frontend.manifest_sha256(copy.assets)
|
|
485
|
+
if wanted and found and found != wanted:
|
|
486
|
+
problems.append(
|
|
487
|
+
"assets/meta/manifest.json is not the one docker/frontend.lock.json "
|
|
488
|
+
"names: run splent product:build"
|
|
489
|
+
)
|
|
490
|
+
if not copy.installed:
|
|
491
|
+
return problems
|
|
492
|
+
import importlib.metadata
|
|
493
|
+
|
|
494
|
+
distribution = entry.get("distribution") or base_name
|
|
495
|
+
try:
|
|
496
|
+
installed = importlib.metadata.version(distribution)
|
|
497
|
+
except importlib.metadata.PackageNotFoundError:
|
|
498
|
+
installed = None
|
|
499
|
+
if entry.get("version") and installed != entry["version"]:
|
|
500
|
+
problems.append(
|
|
501
|
+
f"pip installed {installed or 'no version'}, and "
|
|
502
|
+
f"docker/frontend.lock.json names {entry['version']}"
|
|
503
|
+
)
|
|
504
|
+
if found is None:
|
|
505
|
+
problems.append(
|
|
506
|
+
f"it was installed {_channel(distribution)}, without the bundle its "
|
|
507
|
+
"release built: release it again, so its wheel and its tag carry it"
|
|
508
|
+
)
|
|
509
|
+
return problems
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def _channel(distribution: str) -> str:
|
|
513
|
+
"""How pip installed DISTRIBUTION: from its git tag (git+) or a wheel."""
|
|
514
|
+
import importlib.metadata
|
|
515
|
+
import json
|
|
516
|
+
|
|
517
|
+
try:
|
|
518
|
+
direct = importlib.metadata.distribution(distribution).read_text(
|
|
519
|
+
"direct_url.json"
|
|
520
|
+
)
|
|
521
|
+
url = json.loads(direct or "{}")
|
|
522
|
+
except (importlib.metadata.PackageNotFoundError, ValueError):
|
|
523
|
+
url = {}
|
|
524
|
+
if "vcs_info" in url:
|
|
525
|
+
return f"from its git tag (git+{url.get('url', '')})"
|
|
526
|
+
return "from a wheel"
|
|
527
|
+
|
|
528
|
+
|
|
405
529
|
def _feature_ref(name: str) -> str:
|
|
406
530
|
"""*name* as typed, with the ``splent_feature_`` prefix on the feature's
|
|
407
531
|
own name: ``auth`` and ``splent_io/auth`` are ``splent_feature_auth``
|
|
@@ -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
|
|
|
@@ -62,6 +62,10 @@ LEGACY_REQUIREMENTS_FILE = "requirements.prod.txt"
|
|
|
62
62
|
# Dockerfile applies it as a constraint on every pip install, so two builds of
|
|
63
63
|
# one commit install the same set. Versioned in docker/.
|
|
64
64
|
LOCK_FILE = "requirements.lock"
|
|
65
|
+
# What the features' bundles are and ship, which the image verifies the
|
|
66
|
+
# installed ones against (splent_cli.utils.frontend_check). Versioned in
|
|
67
|
+
# docker/ too.
|
|
68
|
+
FRONTEND_LOCK_FILE = "frontend.lock.json"
|
|
65
69
|
LOCK_HEADER = (
|
|
66
70
|
"# Every third-party package the production image installs, at the version\n"
|
|
67
71
|
"# the last build resolved. Written by splent product:build and\n"
|
|
@@ -1202,6 +1206,30 @@ def _contracts_snapshot(workspace: str, declared_features: Sequence[str]) -> dic
|
|
|
1202
1206
|
return {"format": CONTRACTS_FORMAT, "features": features}
|
|
1203
1207
|
|
|
1204
1208
|
|
|
1209
|
+
def write_frontend_lock(
|
|
1210
|
+
workspace: str, product_path: str, data: dict, declared_features: Sequence[str]
|
|
1211
|
+
) -> str:
|
|
1212
|
+
"""Write docker/frontend.lock.json for the features the image installs.
|
|
1213
|
+
|
|
1214
|
+
For each one with frontend/, the release, the sha256 of the manifest its
|
|
1215
|
+
bundle carries and what the bundle ships, with the provider that
|
|
1216
|
+
satisfies each of its peers; the shell; and the features still built in
|
|
1217
|
+
legacy mode (splent_cli.utils.frontend_check.lock). The production
|
|
1218
|
+
image checks every bundle pip installed against it, and the product's
|
|
1219
|
+
CI audits what they ship from it. Returns its path.
|
|
1220
|
+
"""
|
|
1221
|
+
from splent_cli.utils import frontend_check
|
|
1222
|
+
|
|
1223
|
+
check = frontend_check.check_product(workspace, product_path, declared_features)
|
|
1224
|
+
path = os.path.join(product_path, "docker", FRONTEND_LOCK_FILE)
|
|
1225
|
+
atomic_write(
|
|
1226
|
+
path,
|
|
1227
|
+
frontend_check.lock_text(frontend_check.lock(check, data)),
|
|
1228
|
+
dir_owner=True,
|
|
1229
|
+
)
|
|
1230
|
+
return path
|
|
1231
|
+
|
|
1232
|
+
|
|
1205
1233
|
def _not_shipped(directory, names):
|
|
1206
1234
|
"""Bytecode a local run of a feature left behind, never part of the bundle."""
|
|
1207
1235
|
return {n for n in names if n == "__pycache__" or n.endswith(".pyc")}
|
|
@@ -1481,7 +1509,8 @@ def write_deploy_artifacts(
|
|
|
1481
1509
|
* ``[tool.splent.runtime]`` in pyproject.toml, the framework and CLI the
|
|
1482
1510
|
image installs from PyPI;
|
|
1483
1511
|
* ``splent.contracts.json`` and ``docker/spl/``, the refinement metadata
|
|
1484
|
-
and the UVL model the image reads
|
|
1512
|
+
and the UVL model the image reads;
|
|
1513
|
+
* ``docker/frontend.lock.json``, the bundles the image verifies.
|
|
1485
1514
|
|
|
1486
1515
|
All of them are versioned with the product. product:build and
|
|
1487
1516
|
product:release both go through here, so a release commits exactly what
|
|
@@ -1791,6 +1820,12 @@ def write_deploy_artifacts(
|
|
|
1791
1820
|
written.append(CONTRACTS_SNAPSHOT)
|
|
1792
1821
|
click.echo(f"✅ Created: {contracts_path}")
|
|
1793
1822
|
|
|
1823
|
+
frontend_lock = write_frontend_lock(
|
|
1824
|
+
workspace, product_path, data, declared_features
|
|
1825
|
+
)
|
|
1826
|
+
written.append(f"docker/{FRONTEND_LOCK_FILE}")
|
|
1827
|
+
click.echo(f"✅ Created: {frontend_lock}")
|
|
1828
|
+
|
|
1794
1829
|
staged_model = stage_spl_model(workspace, product, docker_path)
|
|
1795
1830
|
if staged_model:
|
|
1796
1831
|
written.append(f"docker/{SPL_STAGING_DIR}")
|