amsdal_cli 0.9.5__tar.gz → 0.9.7__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.
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/PKG-INFO +1 -1
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/change-logs.md +39 -0
- amsdal_cli-0.9.7/latest-changelogs.md +10 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/__about__.py +1 -1
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/app.py +2 -2
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/callbacks.py +2 -1
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/sync_db.py +2 -2
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/tunnel_db.py +59 -2
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/config/main.py +31 -34
- amsdal_cli-0.9.7/src/amsdal_cli/utils/alias_group.py +111 -0
- amsdal_cli-0.9.7/src/amsdal_cli/utils/check_versions.py +109 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/__init__.py +4 -1
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/uv.lock +131 -108
- amsdal_cli-0.9.5/latest-changelogs.md +0 -12
- amsdal_cli-0.9.5/src/amsdal_cli/utils/alias_group.py +0 -33
- amsdal_cli-0.9.5/src/amsdal_cli/utils/check_versions.py +0 -50
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.editorconfig +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.gitignore +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/.junie/guidelines.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/CONTRIBUTING.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/LICENSE.txt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/README.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/license_check.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/pyproject.toml +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/release_notes/.gitkeep +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/scripts/release.sh +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/scripts/tag_check.sh +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/Third-Party Materials - AMSDAL Dependencies - License Notices.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/config.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/data_classes.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/operation_log.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/comparison.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/data_factory.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/runner.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/api_check/services/storage.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/constants.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/custom_code.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/options.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/data_models/schemas_directory.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/custom_code_extender.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/extenders/options_extender.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/base.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_custom_code_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_options_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_statics_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/cli_transactions_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/loaders/utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/mixins/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/mixins/enrich_schemas_mixin.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/schema_json_loader.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/schemas/utils/merger.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/builder.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/services/mixin.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/build/utils/build_config_file.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/constants.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/ci_cd/templates/github.yml +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/clean/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/clean/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/dependency/sub_commands/dependency_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/deploy/sub_commands/deploy_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/enums.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/constants.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_checkout.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/sub_commands/env_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/environments/utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_add.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_remove.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/external_connections/sub_commands/connection_update.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/constants.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/secret/sub_commands/secret_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/allowlist_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/allowlist/sub_commands/allowlist_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_delete.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_new.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/basic_auth/sub_commands/basic_auth_retrieve.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/security/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/expose_db.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/gen_token.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/get_monitoring_info.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/gen_token/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/gen_token/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/enums.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_external_models.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_frontend_config.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_hook.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_model.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_modifier.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_permission.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_property.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_tests.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/sub_commands/generate_transaction.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/async_transaction.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/hook.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/constructor.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/display_name.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/modifier/version_name.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/property.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/templates/transaction.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/build_base_path.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/cast_to_attribute_type.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/model_attributes.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/async_mode_utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/conftest_utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/function_utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/model_utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/templates/async/conftest.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/templates/sync/conftest.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/type_utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/generate/utils/tests/unit.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/constants.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/apply.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/list.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/make.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/sub_commands/make_contrib.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/migrations/utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.dependencies +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.environment +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal/.secrets +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.amsdal-cli +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.env.template +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/.gitignore +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/README.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/config.yml +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/requirements.txt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/new/templates/src/app.pyt +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/.amsdal-cli +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/README.md +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/config.yml +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/pyproject.toml +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/__about__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/models/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/transactions/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/plugin/templates/src/transactions/example_transaction.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/build.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/config_updater.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/credentials.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/initialize.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/meta.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/migrate_models.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/model_generator.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/register_connection/utils/tables.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/restore/enums.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/models_watch_filter.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/filters/static_files_watch_filter.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/services/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/services/supervisor.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/serve/utils.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/tests/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/tests/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/models.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/verify_json_model.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/verify/utils/verify_python_file.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/app.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/command.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/sub_commands/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/worker/sub_commands/run.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/config/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/main.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/py.typed +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/services/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/services/gen_token.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/__init__.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/auth.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/cli_config.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/copier.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/markdown_patch.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/render_template.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/schema_repository.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/text.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/base.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/dummy.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/enums.py +0 -0
- {amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/utils/vcs/git.py +0 -0
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
## [v0.9.7](https://pypi.org/project/amsdal_cli/0.9.7/) - 2026-06-23
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
- `cloud tunnel-db` now fails fast with a clear, actionable error when the local
|
|
7
|
+
port is already in use, instead of doing all the build/auth/remote work first
|
|
8
|
+
and then dying with a cryptic "might be in use" message from sshtunnel. The
|
|
9
|
+
preflight check runs before anything else and suggests picking another port
|
|
10
|
+
with `--local-port <PORT>`.
|
|
11
|
+
|
|
12
|
+
## [v0.9.6](https://pypi.org/project/amsdal_cli/0.9.6/) - 2026-06-23
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- CLI start-up is significantly faster. Command modules are now imported lazily,
|
|
18
|
+
so running a single command no longer imports every command tree (and their
|
|
19
|
+
heavy dependencies such as pandas and the AMSDAL manager) on each invocation —
|
|
20
|
+
only the module for the invoked command is loaded.
|
|
21
|
+
- The latest-version check against PyPI is now cached on disk for 20 minutes, so
|
|
22
|
+
most commands no longer make blocking network requests on start-up. The check
|
|
23
|
+
also applies a request timeout and no longer lets a network error (e.g. an
|
|
24
|
+
unreachable PyPI) crash the CLI.
|
|
25
|
+
- GitPython is now imported only for projects that use git version control, and
|
|
26
|
+
the `cloud sync-db` database drivers are imported lazily, further reducing
|
|
27
|
+
start-up overhead.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `cloud tunnel-db` now closes promptly instead of hanging until the database
|
|
32
|
+
client disconnects: the SSH transport is force-closed on stop so connection
|
|
33
|
+
handler threads exit immediately.
|
|
34
|
+
- `cloud tunnel-db` now connects to the bastion on the correct SSH port and uses
|
|
35
|
+
a longer SSH handshake timeout with a keep-alive so idle tunnels are not
|
|
36
|
+
dropped. It runs a fast reachability pre-check that reports a clear, actionable
|
|
37
|
+
error instead of hanging for ~2 minutes when the bastion cannot be reached, and
|
|
38
|
+
verifies the tunnel is actually up before reporting success.
|
|
39
|
+
|
|
1
40
|
## [v0.9.5](https://pypi.org/project/amsdal_cli/0.9.5/) - 2026-06-17
|
|
2
41
|
|
|
3
42
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## [v0.9.7](https://pypi.org/project/amsdal_cli/0.9.7/) - 2026-06-23
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
- `cloud tunnel-db` now fails fast with a clear, actionable error when the local
|
|
7
|
+
port is already in use, instead of doing all the build/auth/remote work first
|
|
8
|
+
and then dying with a cryptic "might be in use" message from sshtunnel. The
|
|
9
|
+
preflight check runs before anything else and suggests picking another port
|
|
10
|
+
with `--local-port <PORT>`.
|
|
@@ -2,7 +2,7 @@ import typer
|
|
|
2
2
|
import typer.rich_utils
|
|
3
3
|
|
|
4
4
|
from amsdal_cli.commands.callbacks import init_app_context
|
|
5
|
-
from amsdal_cli.utils.alias_group import
|
|
5
|
+
from amsdal_cli.utils.alias_group import LazyAliasGroup
|
|
6
6
|
from amsdal_cli.utils.markdown_patch import get_custom_help_text
|
|
7
7
|
|
|
8
8
|
# patch typer to use custom help text
|
|
@@ -13,5 +13,5 @@ app = typer.Typer(
|
|
|
13
13
|
callback=init_app_context,
|
|
14
14
|
invoke_without_command=True,
|
|
15
15
|
rich_markup_mode='markdown',
|
|
16
|
-
cls=
|
|
16
|
+
cls=LazyAliasGroup,
|
|
17
17
|
)
|
|
@@ -4,7 +4,6 @@ from json import JSONDecodeError
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
import typer
|
|
7
|
-
from amsdal_utils.config.manager import AmsdalConfigManager
|
|
8
7
|
from rich import print as rprint
|
|
9
8
|
from typer import Option
|
|
10
9
|
|
|
@@ -33,6 +32,8 @@ def init_app_context(
|
|
|
33
32
|
Returns:
|
|
34
33
|
None
|
|
35
34
|
"""
|
|
35
|
+
from amsdal_utils.config.manager import AmsdalConfigManager
|
|
36
|
+
|
|
36
37
|
from amsdal_cli.commands.cloud.environments.utils import get_current_env
|
|
37
38
|
from amsdal_cli.config.main import settings
|
|
38
39
|
from amsdal_cli.utils.check_versions import check_latest_amsdal_version
|
|
@@ -4,10 +4,8 @@ import typing
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
|
-
import amsdal_glue as glue
|
|
8
7
|
import typer
|
|
9
8
|
import yaml
|
|
10
|
-
from amsdal_data.connections.db_alias_map import CONNECTION_BACKEND_ALIASES
|
|
11
9
|
from rich import print as rprint
|
|
12
10
|
from typer import Option
|
|
13
11
|
|
|
@@ -213,6 +211,8 @@ def _build_config(secrets: dict[str, str], db_type: DBType) -> Path:
|
|
|
213
211
|
|
|
214
212
|
|
|
215
213
|
def _copy_data(config: 'AmsdalConfig', connection_names: list[tuple[str, str]]) -> None:
|
|
214
|
+
import amsdal_glue as glue
|
|
215
|
+
from amsdal_data.connections.db_alias_map import CONNECTION_BACKEND_ALIASES
|
|
216
216
|
from amsdal_utils.utils.classes import import_class
|
|
217
217
|
|
|
218
218
|
for from_name, to_name in connection_names:
|
{amsdal_cli-0.9.5 → amsdal_cli-0.9.7}/src/amsdal_cli/commands/cloud/sub_commands/tunnel_db.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import socket
|
|
2
3
|
import tempfile
|
|
3
4
|
import typing
|
|
4
5
|
from pathlib import Path
|
|
@@ -10,6 +11,19 @@ from typer import Option
|
|
|
10
11
|
from amsdal_cli.commands.cloud.app import cloud_sub_app
|
|
11
12
|
from amsdal_cli.utils.auth import ensure_authenticated
|
|
12
13
|
|
|
14
|
+
# The cloud bastion exposes SSH on this port (not the default 22).
|
|
15
|
+
BASTION_SSH_PORT = 5444
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_local_port_available(port: int) -> bool:
|
|
19
|
+
"""Return True if the tunnel can bind 127.0.0.1:<port>, False if it is taken."""
|
|
20
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
21
|
+
try:
|
|
22
|
+
sock.bind(('127.0.0.1', port))
|
|
23
|
+
except OSError:
|
|
24
|
+
return False
|
|
25
|
+
return True
|
|
26
|
+
|
|
13
27
|
|
|
14
28
|
def _generate_temp_ssh_key() -> tuple[bytes, bytes]:
|
|
15
29
|
"""Generate an ephemeral RSA key pair in OpenSSH format."""
|
|
@@ -72,6 +86,18 @@ def tunnel_db_command(
|
|
|
72
86
|
if cli_config.verbose:
|
|
73
87
|
rprint(rich_info(f'Opening database tunnel for environment: {rich_highlight(env_name)}'))
|
|
74
88
|
|
|
89
|
+
# Preflight: bail before any build/auth/remote work if the local port is taken,
|
|
90
|
+
# otherwise sshtunnel fails later with a cryptic "might be in use" message.
|
|
91
|
+
if not _is_local_port_available(local_port):
|
|
92
|
+
rprint(
|
|
93
|
+
rich_error(
|
|
94
|
+
f'Local port {rich_highlight(str(local_port))} is already in use. '
|
|
95
|
+
f'Close whatever is using it, or pick another port with '
|
|
96
|
+
f'{rich_highlight("--local-port <PORT>")}.'
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
return
|
|
100
|
+
|
|
75
101
|
with tempfile.TemporaryDirectory() as _temp_dir:
|
|
76
102
|
output_path: Path = Path(_temp_dir)
|
|
77
103
|
|
|
@@ -106,16 +132,44 @@ def tunnel_db_command(
|
|
|
106
132
|
|
|
107
133
|
tunnel: SSHTunnelForwarder | None = None
|
|
108
134
|
|
|
135
|
+
# sshtunnel's module-default SSH_TIMEOUT is 0.1s, far too short to complete
|
|
136
|
+
# an SSH handshake to a remote bastion, so start() can fail to connect.
|
|
137
|
+
import sshtunnel
|
|
138
|
+
|
|
139
|
+
sshtunnel.SSH_TIMEOUT = 10.0
|
|
140
|
+
|
|
141
|
+
# Preflight: paramiko opens the bastion socket with no connect timeout, so an
|
|
142
|
+
# unreachable bastion port hangs for the full OS TCP timeout (~2 min). Fail
|
|
143
|
+
# fast with a clear, actionable message instead.
|
|
144
|
+
try:
|
|
145
|
+
with socket.create_connection((details['bastion_host'], BASTION_SSH_PORT), timeout=10):
|
|
146
|
+
pass
|
|
147
|
+
except OSError as e:
|
|
148
|
+
rprint(
|
|
149
|
+
rich_error(
|
|
150
|
+
f'Cannot reach bastion {details["bastion_host"]}:{BASTION_SSH_PORT} ({e}). '
|
|
151
|
+
f'Check that your IP is allowed on the bastion security group and that '
|
|
152
|
+
f'outbound port {BASTION_SSH_PORT} is not blocked by your network/VPN.'
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
os.unlink(key_path)
|
|
156
|
+
return
|
|
157
|
+
|
|
109
158
|
try:
|
|
110
159
|
tunnel = SSHTunnelForwarder(
|
|
111
|
-
(details['bastion_host'],
|
|
160
|
+
(details['bastion_host'], BASTION_SSH_PORT),
|
|
112
161
|
ssh_username=details['ssh_user'],
|
|
113
162
|
ssh_pkey=key_path,
|
|
114
163
|
remote_bind_address=(details['rds_host'], int(details['rds_port'] or db_port)),
|
|
115
164
|
local_bind_address=('127.0.0.1', local_port),
|
|
165
|
+
set_keepalive=30.0,
|
|
116
166
|
)
|
|
117
167
|
tunnel.start()
|
|
118
168
|
|
|
169
|
+
if not tunnel.is_active or not all(tunnel.tunnel_is_up.values()):
|
|
170
|
+
rprint(rich_error('Failed to establish the database tunnel.'))
|
|
171
|
+
return
|
|
172
|
+
|
|
119
173
|
rprint(
|
|
120
174
|
rich_success(
|
|
121
175
|
f'Tunnel open. Connect your SQL client to {rich_highlight(f"127.0.0.1:{local_port}")}',
|
|
@@ -128,6 +182,9 @@ def tunnel_db_command(
|
|
|
128
182
|
rprint(rich_error(f'Failed to open tunnel: {e}'))
|
|
129
183
|
finally:
|
|
130
184
|
if tunnel is not None:
|
|
131
|
-
|
|
185
|
+
# force=True closes the SSH transport first so active connection
|
|
186
|
+
# handler threads exit immediately; without it stop() blocks
|
|
187
|
+
# joining those threads until the SQL client disconnects.
|
|
188
|
+
tunnel.stop(force=True)
|
|
132
189
|
os.unlink(key_path)
|
|
133
190
|
rprint(rich_info('Tunnel closed.'))
|
|
@@ -1,14 +1,38 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
from types import ModuleType
|
|
3
1
|
from typing import TYPE_CHECKING
|
|
4
2
|
from typing import Any
|
|
5
3
|
from typing import TypeAlias
|
|
6
4
|
|
|
7
5
|
from pydantic import Field
|
|
8
|
-
from pydantic import field_validator
|
|
9
6
|
from pydantic_settings import BaseSettings
|
|
10
7
|
from pydantic_settings import SettingsConfigDict
|
|
11
8
|
|
|
9
|
+
# Maps each top-level command name (including its comma-separated aliases, exactly
|
|
10
|
+
# as registered via ``@app.command`` / ``app.add_typer``) to the module whose
|
|
11
|
+
# ``.command`` submodule registers it on the root Typer ``app``.
|
|
12
|
+
#
|
|
13
|
+
# This is the single source of truth for lazy command loading: the root group
|
|
14
|
+
# (``LazyAliasGroup``) imports a module only when its command is actually invoked,
|
|
15
|
+
# instead of importing all 16 command trees on every CLI start. See
|
|
16
|
+
# ``amsdal_cli/utils/alias_group.py``.
|
|
17
|
+
COMMAND_REGISTRY: dict[str, str] = {
|
|
18
|
+
'api-check': 'amsdal_cli.commands.api_check',
|
|
19
|
+
'new, n': 'amsdal_cli.commands.new',
|
|
20
|
+
'plugin': 'amsdal_cli.commands.plugin',
|
|
21
|
+
'generate, gen, g': 'amsdal_cli.commands.generate',
|
|
22
|
+
'verify, vrf, v': 'amsdal_cli.commands.verify',
|
|
23
|
+
'build, bld, b': 'amsdal_cli.commands.build',
|
|
24
|
+
'clean, cln': 'amsdal_cli.commands.clean',
|
|
25
|
+
'serve, srv, s': 'amsdal_cli.commands.serve',
|
|
26
|
+
'restore, rst': 'amsdal_cli.commands.restore',
|
|
27
|
+
'cloud, cld': 'amsdal_cli.commands.cloud',
|
|
28
|
+
'migrations, migs, mgs': 'amsdal_cli.commands.migrations',
|
|
29
|
+
'ci-cd': 'amsdal_cli.commands.ci_cd',
|
|
30
|
+
'tests, test': 'amsdal_cli.commands.tests',
|
|
31
|
+
'worker, w': 'amsdal_cli.commands.worker',
|
|
32
|
+
'register-connection, reg-conn': 'amsdal_cli.commands.register_connection',
|
|
33
|
+
'gen-token, gen_token, gt': 'amsdal_cli.commands.gen_token',
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
|
|
13
37
|
class Settings(BaseSettings):
|
|
14
38
|
model_config = SettingsConfigDict(
|
|
@@ -18,43 +42,16 @@ class Settings(BaseSettings):
|
|
|
18
42
|
extra='ignore',
|
|
19
43
|
)
|
|
20
44
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
'amsdal_cli.commands.plugin',
|
|
26
|
-
'amsdal_cli.commands.generate',
|
|
27
|
-
'amsdal_cli.commands.verify',
|
|
28
|
-
'amsdal_cli.commands.build',
|
|
29
|
-
'amsdal_cli.commands.clean',
|
|
30
|
-
'amsdal_cli.commands.serve',
|
|
31
|
-
'amsdal_cli.commands.restore',
|
|
32
|
-
'amsdal_cli.commands.cloud',
|
|
33
|
-
'amsdal_cli.commands.migrations',
|
|
34
|
-
'amsdal_cli.commands.ci_cd',
|
|
35
|
-
'amsdal_cli.commands.tests',
|
|
36
|
-
'amsdal_cli.commands.worker',
|
|
37
|
-
'amsdal_cli.commands.register_connection',
|
|
38
|
-
'amsdal_cli.commands.gen_token',
|
|
39
|
-
]
|
|
40
|
-
)
|
|
45
|
+
# Module paths of the registered commands. Kept for backwards compatibility;
|
|
46
|
+
# commands are now loaded lazily through COMMAND_REGISTRY, so importing this
|
|
47
|
+
# settings object no longer imports any command module.
|
|
48
|
+
COMMANDS: list[str] = Field(default=list(COMMAND_REGISTRY.values()))
|
|
41
49
|
|
|
42
50
|
CHECK_AMSDAL_VERSIONS: bool = True
|
|
43
51
|
"""If True, the latest version of the amsdal modules will be checked and displayed."""
|
|
44
52
|
AMSDAL_APPLICATION_UUID: str | None = None
|
|
45
53
|
"""The UUID of the application."""
|
|
46
54
|
|
|
47
|
-
@field_validator('COMMANDS')
|
|
48
|
-
@classmethod
|
|
49
|
-
def load_commands(cls, value: list[Any]) -> list[ModuleType]:
|
|
50
|
-
commands: list[ModuleType] = []
|
|
51
|
-
|
|
52
|
-
for module in value:
|
|
53
|
-
if isinstance(module, str):
|
|
54
|
-
commands.append(importlib.import_module(f'{module}.command'))
|
|
55
|
-
|
|
56
|
-
return commands
|
|
57
|
-
|
|
58
55
|
|
|
59
56
|
if TYPE_CHECKING:
|
|
60
57
|
base: TypeAlias = Settings
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import re
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from click import Command
|
|
6
|
+
from typer import Context
|
|
7
|
+
from typer.core import TyperGroup
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AliasGroup(TyperGroup):
|
|
11
|
+
"""
|
|
12
|
+
A custom TyperGroup that supports aliasing commands.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
_CMD_SPLIT_P = r'[,| ?\/]'
|
|
16
|
+
|
|
17
|
+
def get_command(self, ctx: Context, cmd_name: str) -> Command | None: # type: ignore[override]
|
|
18
|
+
"""
|
|
19
|
+
Get the command associated with the given command name.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
ctx (Context): The Typer context object.
|
|
23
|
+
cmd_name (str): The name of the command to retrieve.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Command | None: The command object if found, otherwise None.
|
|
27
|
+
"""
|
|
28
|
+
cmd_name = self._group_cmd_name(cmd_name)
|
|
29
|
+
return super().get_command(ctx, cmd_name)
|
|
30
|
+
|
|
31
|
+
def _group_cmd_name(self, default_name: str) -> str:
|
|
32
|
+
for cmd in self.commands.values():
|
|
33
|
+
if cmd.name and default_name in re.split(self._CMD_SPLIT_P, cmd.name):
|
|
34
|
+
return cmd.name
|
|
35
|
+
return default_name
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LazyAliasGroup(AliasGroup):
|
|
39
|
+
"""
|
|
40
|
+
Root command group that imports a command's module only when it is invoked.
|
|
41
|
+
|
|
42
|
+
The default flow imported all 16 command trees on every CLI start (dragging in
|
|
43
|
+
pandas, the AMSDAL manager, etc.) just to register them. Instead, this group
|
|
44
|
+
starts empty and, on demand, imports the single module that registers the
|
|
45
|
+
requested command onto the root Typer ``app``, then materialises it.
|
|
46
|
+
|
|
47
|
+
``--help`` and shell completion still need the full list, so ``list_commands``
|
|
48
|
+
loads everything; a specific command invocation only pays for its own module.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
52
|
+
super().__init__(*args, **kwargs)
|
|
53
|
+
|
|
54
|
+
from amsdal_cli.config.main import COMMAND_REGISTRY
|
|
55
|
+
|
|
56
|
+
# token (canonical name or alias) -> module that registers it
|
|
57
|
+
self._token_to_module: dict[str, str] = {}
|
|
58
|
+
for full_name, module in COMMAND_REGISTRY.items():
|
|
59
|
+
for token in re.split(self._CMD_SPLIT_P, full_name):
|
|
60
|
+
if token:
|
|
61
|
+
self._token_to_module[token] = module
|
|
62
|
+
|
|
63
|
+
# ordered, de-duplicated module list for whole-registry loads
|
|
64
|
+
self._all_modules: list[str] = list(dict.fromkeys(COMMAND_REGISTRY.values()))
|
|
65
|
+
self._loaded_modules: set[str] = set()
|
|
66
|
+
|
|
67
|
+
def _sync_from_app(self) -> None:
|
|
68
|
+
"""Materialise any newly registered Typer commands/groups into this group."""
|
|
69
|
+
from typer.main import get_command_from_info
|
|
70
|
+
from typer.main import get_group_from_info
|
|
71
|
+
|
|
72
|
+
from amsdal_cli.app import app
|
|
73
|
+
|
|
74
|
+
markup = self.rich_markup_mode
|
|
75
|
+
short = app.pretty_exceptions_short
|
|
76
|
+
|
|
77
|
+
for command_info in app.registered_commands:
|
|
78
|
+
command = get_command_from_info(command_info, pretty_exceptions_short=short, rich_markup_mode=markup)
|
|
79
|
+
if command.name and command.name not in self.commands:
|
|
80
|
+
self.commands[command.name] = command
|
|
81
|
+
|
|
82
|
+
for group_info in app.registered_groups:
|
|
83
|
+
group = get_group_from_info(group_info, pretty_exceptions_short=short, rich_markup_mode=markup)
|
|
84
|
+
if group.name and group.name not in self.commands:
|
|
85
|
+
self.commands[group.name] = group
|
|
86
|
+
|
|
87
|
+
def _load_module(self, module: str) -> None:
|
|
88
|
+
if module in self._loaded_modules:
|
|
89
|
+
return
|
|
90
|
+
importlib.import_module(f'{module}.command')
|
|
91
|
+
self._loaded_modules.add(module)
|
|
92
|
+
self._sync_from_app()
|
|
93
|
+
|
|
94
|
+
def _load_all(self) -> None:
|
|
95
|
+
for module in self._all_modules:
|
|
96
|
+
self._load_module(module)
|
|
97
|
+
|
|
98
|
+
def get_command(self, ctx: Context, cmd_name: str) -> Command | None: # type: ignore[override]
|
|
99
|
+
if self._group_cmd_name(cmd_name) not in self.commands:
|
|
100
|
+
module = self._token_to_module.get(cmd_name)
|
|
101
|
+
if module is not None:
|
|
102
|
+
self._load_module(module)
|
|
103
|
+
else:
|
|
104
|
+
# Unknown token (e.g. a typo): load everything so resolution and
|
|
105
|
+
# the "no such command" suggestions behave exactly as before.
|
|
106
|
+
self._load_all()
|
|
107
|
+
return super().get_command(ctx, cmd_name)
|
|
108
|
+
|
|
109
|
+
def list_commands(self, ctx: Context) -> list[str]: # type: ignore[override]
|
|
110
|
+
self._load_all()
|
|
111
|
+
return super().list_commands(ctx)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import json
|
|
3
|
+
import tempfile
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from rich import print as rprint
|
|
9
|
+
|
|
10
|
+
from amsdal_cli.utils.text import rich_command
|
|
11
|
+
from amsdal_cli.utils.text import rich_highlight_version
|
|
12
|
+
|
|
13
|
+
AMSDAL_MODULES = [
|
|
14
|
+
'amsdal_utils',
|
|
15
|
+
'amsdal_data',
|
|
16
|
+
'amsdal_models',
|
|
17
|
+
'amsdal',
|
|
18
|
+
'amsdal_server',
|
|
19
|
+
'amsdal_cli',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# Latest versions on PyPI change rarely, but this check runs on every project
|
|
23
|
+
# command and makes one blocking request per module. Cache the fetched versions
|
|
24
|
+
# on disk for 20 minutes so most invocations skip the network entirely.
|
|
25
|
+
_CACHE_TTL_SECONDS = 20 * 60
|
|
26
|
+
_CACHE_PATH = Path(tempfile.gettempdir()) / 'amsdal_cli_latest_versions.json'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _read_cached_versions() -> dict[str, str] | None:
|
|
30
|
+
"""Return cached ``{module: latest_version}`` if the cache is fresh, else None."""
|
|
31
|
+
try:
|
|
32
|
+
payload = json.loads(_CACHE_PATH.read_text())
|
|
33
|
+
fetched_at = float(payload['fetched_at'])
|
|
34
|
+
versions = payload['versions']
|
|
35
|
+
except (OSError, ValueError, KeyError, TypeError):
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
if not isinstance(versions, dict) or time.time() - fetched_at > _CACHE_TTL_SECONDS:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
return versions
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _write_cached_versions(versions: dict[str, str]) -> None:
|
|
45
|
+
try:
|
|
46
|
+
_CACHE_PATH.write_text(json.dumps({'fetched_at': time.time(), 'versions': versions}))
|
|
47
|
+
except OSError:
|
|
48
|
+
# Caching is best-effort; a failure here must never break the command.
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _fetch_latest_versions() -> dict[str, str]:
|
|
53
|
+
"""Fetch the latest version of each AMSDAL module from PyPI.
|
|
54
|
+
|
|
55
|
+
Modules that cannot be reached (network/HTTP/parse errors) are skipped rather
|
|
56
|
+
than raising, so an offline machine or a PyPI hiccup never breaks the CLI.
|
|
57
|
+
"""
|
|
58
|
+
versions: dict[str, str] = {}
|
|
59
|
+
|
|
60
|
+
with httpx.Client(timeout=5.0) as session:
|
|
61
|
+
for amsdal_module in AMSDAL_MODULES:
|
|
62
|
+
try:
|
|
63
|
+
response = session.get(f'https://pypi.org/pypi/{amsdal_module}/json')
|
|
64
|
+
versions[amsdal_module] = response.json()['info']['version']
|
|
65
|
+
except (httpx.HTTPError, KeyError, ValueError):
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
return versions
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def check_latest_amsdal_version() -> None:
|
|
72
|
+
"""
|
|
73
|
+
Check the latest version of AMSDAL modules and suggest upgrades if necessary.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
None
|
|
77
|
+
"""
|
|
78
|
+
latest_versions = _read_cached_versions()
|
|
79
|
+
if latest_versions is None:
|
|
80
|
+
latest_versions = _fetch_latest_versions()
|
|
81
|
+
_write_cached_versions(latest_versions)
|
|
82
|
+
|
|
83
|
+
modules_to_update: list[str] = []
|
|
84
|
+
|
|
85
|
+
for amsdal_module in AMSDAL_MODULES:
|
|
86
|
+
latest_version = latest_versions.get(amsdal_module)
|
|
87
|
+
if not latest_version:
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
module = importlib.import_module(f'{amsdal_module}.__about__')
|
|
92
|
+
except ImportError:
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
current_version = module.__version__
|
|
96
|
+
|
|
97
|
+
if current_version == latest_version:
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
rprint(
|
|
101
|
+
f'You are using {amsdal_module} version {rich_highlight_version(current_version)}, '
|
|
102
|
+
f'however version {rich_highlight_version(latest_version)} is available.'
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
modules_to_update.append(amsdal_module)
|
|
106
|
+
|
|
107
|
+
if modules_to_update:
|
|
108
|
+
upgrade_command = 'pip install --upgrade ' + ' '.join(modules_to_update)
|
|
109
|
+
rprint(f"You should consider upgrading via the '{rich_command(upgrade_command)}' command.")
|
|
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
from amsdal_cli.utils.vcs.base import VCSBaseService
|
|
4
4
|
from amsdal_cli.utils.vcs.dummy import DummyVSCService
|
|
5
5
|
from amsdal_cli.utils.vcs.enums import VCSOptions
|
|
6
|
-
from amsdal_cli.utils.vcs.git import GitService
|
|
7
6
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
9
8
|
from amsdal_cli.utils.cli_config import CliConfig
|
|
@@ -11,6 +10,10 @@ if TYPE_CHECKING:
|
|
|
11
10
|
|
|
12
11
|
def get_vcs_service(config: 'CliConfig') -> VCSBaseService:
|
|
13
12
|
if config.vcs == VCSOptions.git:
|
|
13
|
+
# Imported lazily so projects that don't use git (the default) never pay
|
|
14
|
+
# the cost of loading GitPython, which runs on every project command.
|
|
15
|
+
from amsdal_cli.utils.vcs.git import GitService
|
|
16
|
+
|
|
14
17
|
return GitService(config)
|
|
15
18
|
return DummyVSCService(config)
|
|
16
19
|
|