nextmv 1.1.3.dev2__tar.gz → 1.2.0.dev1__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.
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/.gitignore +1 -1
- {nextmv-1.1.3.dev2/nextmv/cli → nextmv-1.2.0.dev1}/CONTRIBUTING.md +211 -18
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/PKG-INFO +2 -7
- nextmv-1.2.0.dev1/nextmv/__about__.py +1 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/__init__.py +2 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/account/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/push.py +4 -5
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/subscription/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/create.py +26 -5
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/__init__.py +2 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/disable.py +2 -3
- nextmv-1.2.0.dev1/nextmv/cli/cloud/sso/domain/__init__.py +24 -0
- nextmv-1.2.0.dev1/nextmv/cli/cloud/sso/domain/delete.py +67 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/enable.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/configuration/config.py +6 -7
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/configuration/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/delete.py +2 -3
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/sync.py +2 -4
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/main.py +5 -6
- nextmv-1.2.0.dev1/nextmv/cli/manifest/__init__.py +21 -0
- nextmv-1.2.0.dev1/nextmv/cli/manifest/init.py +104 -0
- nextmv-1.2.0.dev1/nextmv/cli/manifest/validate.py +53 -0
- nextmv-1.2.0.dev1/nextmv/cli/message.py +316 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_run.py +22 -4
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/client.py +5 -1
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/sso.py +24 -1
- nextmv-1.2.0.dev1/nextmv/content_format.py +53 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/input.py +0 -2
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/manifest.py +56 -1
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/run.py +40 -0
- nextmv-1.2.0.dev1/nextmv/templates/binary_json_app.yaml +24 -0
- nextmv-1.2.0.dev1/nextmv/templates/binary_multi-file_app.yaml +32 -0
- nextmv-1.2.0.dev1/nextmv/templates/go_json_app.yaml +24 -0
- nextmv-1.2.0.dev1/nextmv/templates/go_multi-file_app.yaml +32 -0
- nextmv-1.2.0.dev1/nextmv/templates/java_json_app.yaml +19 -0
- nextmv-1.2.0.dev1/nextmv/templates/java_multi-file_app.yaml +27 -0
- nextmv-1.2.0.dev1/nextmv/templates/python_json_app.yaml +21 -0
- nextmv-1.2.0.dev1/nextmv/templates/python_multi-file_app.yaml +29 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/pyproject.toml +2 -9
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cli/test_configuration.py +1 -1
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/integration/cloud/test_integration_cloud.py +15 -4
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_manifest.py +120 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/uv.lock +35 -305
- nextmv-1.1.3.dev2/nextmv/__about__.py +0 -1
- nextmv-1.1.3.dev2/nextmv/cli/confirm.py +0 -34
- nextmv-1.1.3.dev2/nextmv/cli/mcp/__init__.py +0 -26
- nextmv-1.1.3.dev2/nextmv/cli/mcp/serve.py +0 -76
- nextmv-1.1.3.dev2/nextmv/cli/mcp/server.py +0 -1927
- nextmv-1.1.3.dev2/nextmv/cli/message.py +0 -170
- nextmv-1.1.3.dev2/tests/README.md +0 -91
- nextmv-1.1.3.dev2/tests/cli/test_mcp.py +0 -622
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/.python-version +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/LICENSE +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/README.md +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/__entrypoint__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/_serialization.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/base_model.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/acceptance/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/account/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/account/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/account/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/account/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/exists.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/app/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/batch/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/data/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/data/upload.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/ensemble/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/input_set/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/exists.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/instance/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/managed_input/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/app/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/app/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/app/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/app/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/app/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/subscription/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/subscription/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/subscription/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/subscription/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/version/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/version/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/version/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/version/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/marketplace/version/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/cancel.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/input.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/logs.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/run/track.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/scenario/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/secrets/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/start.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/stop.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/shadow/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/sso/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/start.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/stop.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/switchback/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/upload/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/upload/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/exists.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/cloud/version/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/community/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/community/clone.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/community/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/configuration/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/configuration/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/configuration/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/register.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/registered.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/app/update.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/create.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/get.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/input.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/list.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/logs.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/metadata.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/local/run/visuals.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/options.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cli/version.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/account.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_acceptance.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_batch_scenario.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_ensemble.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_input_set.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_instance.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_managed_input.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_secrets.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_shadow.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_switchback.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_utils.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/application/_version.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/assets.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/community.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/input_set.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/instance.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/integration.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/marketplace.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/package.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/scenario.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/secrets.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/shadow.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/switchback.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/url.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/cloud/version.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/.gitignore +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/README.md +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/app.yaml +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/input.json +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/main.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/deprecated.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/application.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/executor.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/local.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/registry.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/local/runner.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/logger.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/model.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/options.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/output.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/polling.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/safe.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/nextmv/status.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cli/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cli/test_main.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cli/test_version.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/app.yaml +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/test_client.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/test_instance.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/test_package.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/cloud/test_scenario.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/integration/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/integration/cloud/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/integration/cloud/test_integration_marketplace.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/local/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/local/test_application.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/local/test_executor.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/local/test_registry.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/local/test_runner.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options1.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options2.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options3.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options4.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options5.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options6.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/scripts/options7.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_base_model.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_input.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_inputs/test_data.json +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_logger.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_model.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_options.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_output.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_polling.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_run.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_safe.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_serialization.py +0 -0
- {nextmv-1.1.3.dev2 → nextmv-1.2.0.dev1}/tests/test_version.py +0 -0
|
@@ -1,9 +1,158 @@
|
|
|
1
|
-
# Contributing
|
|
1
|
+
# Contributing
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Welcome to `nextmv`, the general Python SDK for the Nextmv Platform. Please
|
|
4
|
+
read this contributing guide to get set up before submitting a pull request.
|
|
5
|
+
|
|
6
|
+
We recommend you open _this_ directory in a standalone window of your editor.
|
|
7
|
+
From now on, all the commands assume that you are standing at the root of
|
|
8
|
+
_this_ library, i.e. the directory where the `pyproject.toml` file is located.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
Make sure you have the following installed:
|
|
13
|
+
|
|
14
|
+
- [Python][python]: Python 3.10 or higher.
|
|
15
|
+
- [`uv`][uv]: Python package and project manager.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
Sync all the dependencies with:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv sync
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Repeat this command when there are changes to the `pyproject.toml` or `uv.lock`
|
|
26
|
+
files. Always commit changes to the `pyproject.toml` and `uv.lock` files.
|
|
27
|
+
|
|
28
|
+
## Adding dependencies
|
|
29
|
+
|
|
30
|
+
- If you need to add a new dependency to the library, add it with:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv add <PACKAGE_NAME>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- If you need to add a new _optional_ dependency to the library, add it with:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv add <PACKAGE_NAME> --optional <EXTRA_NAME>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- If you need to add a _development_ dependency, i.e. a package that is only
|
|
43
|
+
needed for development and testing (like `Nextpipe`, which is used in the
|
|
44
|
+
integration tests), add it with:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv add <PACKAGE_NAME> --dev
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Linting
|
|
51
|
+
|
|
52
|
+
Run the linter with:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv run ruff check .
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Unit tests
|
|
59
|
+
|
|
60
|
+
Run unit tests with:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uv run pytest --ignore=tests/integration
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This will ignore integration tests located in `tests/integration`.
|
|
67
|
+
|
|
68
|
+
## Integration tests
|
|
69
|
+
|
|
70
|
+
Make sure you have a valid Nextmv Cloud API key set in your environment:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
export NEXTMV_API_KEY="<YOUR_API_KEY>"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Run integration tests with:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv run pytest tests/integration -s
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The integration tests use [Nextpipe][nextpipe] to orchestrate a workflow of
|
|
83
|
+
steps that run both in sequence and in parallel. Here is a mermaid diagram of
|
|
84
|
+
the steps that are run in the integration tests. This diagram was generated at
|
|
85
|
+
the time of writing, and may change as the tests are updated.
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
graph LR
|
|
89
|
+
init_app(init_app)
|
|
90
|
+
init_app --> community_push
|
|
91
|
+
init_app --> versions
|
|
92
|
+
init_app --> instances
|
|
93
|
+
init_app --> runs
|
|
94
|
+
init_app --> input_sets
|
|
95
|
+
init_app --> scenario_tests
|
|
96
|
+
init_app --> shadow_tests
|
|
97
|
+
init_app --> switchback_tests
|
|
98
|
+
init_app --> acceptance_tests
|
|
99
|
+
init_app --> secrets
|
|
100
|
+
init_app --> ensembles
|
|
101
|
+
init_app --> cleanup
|
|
102
|
+
community_push(community_push)
|
|
103
|
+
community_push --> versions
|
|
104
|
+
community_push --> secrets
|
|
105
|
+
versions(versions)
|
|
106
|
+
versions --> instances
|
|
107
|
+
versions --> cleanup
|
|
108
|
+
instances(instances)
|
|
109
|
+
instances --> runs
|
|
110
|
+
instances --> input_sets
|
|
111
|
+
instances --> scenario_tests
|
|
112
|
+
instances --> shadow_tests
|
|
113
|
+
instances --> switchback_tests
|
|
114
|
+
instances --> acceptance_tests
|
|
115
|
+
instances --> ensembles
|
|
116
|
+
instances --> cleanup
|
|
117
|
+
runs(runs)
|
|
118
|
+
runs --> input_sets
|
|
119
|
+
runs --> cleanup
|
|
120
|
+
input_sets(input_sets)
|
|
121
|
+
input_sets --> scenario_tests
|
|
122
|
+
input_sets --> acceptance_tests
|
|
123
|
+
input_sets --> cleanup
|
|
124
|
+
scenario_tests(scenario_tests)
|
|
125
|
+
scenario_tests --> cleanup
|
|
126
|
+
shadow_tests(shadow_tests)
|
|
127
|
+
shadow_tests --> switchback_tests
|
|
128
|
+
shadow_tests --> cleanup
|
|
129
|
+
switchback_tests(switchback_tests)
|
|
130
|
+
switchback_tests --> cleanup
|
|
131
|
+
acceptance_tests(acceptance_tests)
|
|
132
|
+
acceptance_tests --> cleanup
|
|
133
|
+
secrets(secrets)
|
|
134
|
+
secrets --> cleanup
|
|
135
|
+
ensembles(ensembles)
|
|
136
|
+
ensembles --> cleanup
|
|
137
|
+
cleanup(cleanup)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## CLI
|
|
141
|
+
|
|
142
|
+
Whenever you are contributing to the `./nextmv/cli` package, you can run the
|
|
143
|
+
`nextmv` command like this:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv run nextmv <COMMAND>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
This will allow you to directly test the changes you are making to the CLI.
|
|
150
|
+
|
|
151
|
+
### CLI - Style guidelines
|
|
152
|
+
|
|
153
|
+
Here are a few style guidelines to help all of us maintain a high-quality tool,
|
|
154
|
+
that feels unified and consistent. You are required to read and understand
|
|
155
|
+
these guidelines before submitting a pull request.
|
|
7
156
|
|
|
8
157
|
The Nextmv CLI is built using [Typer][typer]. If you don't know Typer, we
|
|
9
158
|
_strongly encourage_ you to read through the [Typer Learn][typer-learn]
|
|
@@ -29,7 +178,7 @@ from the Typer docs:
|
|
|
29
178
|
> The best results for your command line application would be achieved
|
|
30
179
|
> combining both Typer and Rich.
|
|
31
180
|
|
|
32
|
-
|
|
181
|
+
#### CLI - Command structure
|
|
33
182
|
|
|
34
183
|
The logic for command tree organization is based on:
|
|
35
184
|
|
|
@@ -67,7 +216,7 @@ Where:
|
|
|
67
216
|
believe a different structure makes more sense, feel free to propose it in
|
|
68
217
|
your pull request, explaining the reasoning behind it.
|
|
69
218
|
|
|
70
|
-
|
|
219
|
+
#### CLI - File organization
|
|
71
220
|
|
|
72
221
|
Follow these guidelines when organizing files and directories for commands:
|
|
73
222
|
|
|
@@ -142,7 +291,7 @@ Follow these guidelines when organizing files and directories for commands:
|
|
|
142
291
|
pass
|
|
143
292
|
```
|
|
144
293
|
|
|
145
|
-
|
|
294
|
+
#### CLI - Printing
|
|
146
295
|
|
|
147
296
|
When information to the user, i.e., printing to the console, follow these
|
|
148
297
|
guidelines:
|
|
@@ -160,6 +309,12 @@ guidelines:
|
|
|
160
309
|
- `warning`: prints a warning message. Use for non-critical issues.
|
|
161
310
|
- `error`: prints an error and raises an exception. Use for critical issues
|
|
162
311
|
and to return early from commands.
|
|
312
|
+
- `confirmation`: prompts the user for a yes/no confirmation. Returns the
|
|
313
|
+
default value in non-interactive sessions.
|
|
314
|
+
- `choice`: prompts the user to select one option from a list of choices.
|
|
315
|
+
Returns the default value in non-interactive sessions.
|
|
316
|
+
- `directory_path`: prompts the user to enter or select a directory path.
|
|
317
|
+
Returns the default value in non-interactive sessions.
|
|
163
318
|
- For printing `JSON` information, use the `print_json` function in the
|
|
164
319
|
`message.py` file to print JSON output. This ensures consistent formatting
|
|
165
320
|
across the CLI.
|
|
@@ -191,13 +346,20 @@ guidelines:
|
|
|
191
346
|
] = None,
|
|
192
347
|
```
|
|
193
348
|
|
|
194
|
-
|
|
349
|
+
#### CLI - User prompts
|
|
350
|
+
|
|
351
|
+
The `message.py` file provides three interactive prompt functions: `confirmation`,
|
|
352
|
+
`choice`, and `directory_path`. All three handle non-interactive sessions
|
|
353
|
+
gracefully by returning a default value when `stdin` is not a TTY, preventing
|
|
354
|
+
commands from hanging indefinitely.
|
|
355
|
+
|
|
356
|
+
##### CLI - Confirmation
|
|
195
357
|
|
|
196
|
-
For destructive actions (like deletions), use the `
|
|
197
|
-
to ask for user confirmation before proceeding.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
358
|
+
For destructive actions (like deletions), use the `confirmation()` function
|
|
359
|
+
from `message.py` to ask for user confirmation before proceeding. This function
|
|
360
|
+
already handles sensible values used for getting a confirmation from a user.
|
|
361
|
+
Additionally, it handles non-interactive sessions by defaulting to `False` if
|
|
362
|
+
no input can be provided.
|
|
201
363
|
|
|
202
364
|
When using confirmation prompts, follow these guidelines:
|
|
203
365
|
|
|
@@ -211,7 +373,7 @@ Consider the `nextmv cloud app delete` command:
|
|
|
211
373
|
|
|
212
374
|
```python
|
|
213
375
|
if not yes:
|
|
214
|
-
confirm =
|
|
376
|
+
confirm = confirmation(
|
|
215
377
|
f"Are you sure you want to delete application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
216
378
|
)
|
|
217
379
|
|
|
@@ -220,7 +382,35 @@ if not yes:
|
|
|
220
382
|
return
|
|
221
383
|
```
|
|
222
384
|
|
|
223
|
-
|
|
385
|
+
##### CLI - Choice
|
|
386
|
+
|
|
387
|
+
Use the `choice()` function when the user must select one option from a
|
|
388
|
+
predefined list. It renders an interactive selection menu and returns the chosen
|
|
389
|
+
value. In non-interactive sessions, the `default` value is returned instead.
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
selected = choice(
|
|
393
|
+
"Select an instance to use:",
|
|
394
|
+
choices=["instance-a", "instance-b", "instance-c"],
|
|
395
|
+
default="instance-a",
|
|
396
|
+
)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
##### CLI - Directory path
|
|
400
|
+
|
|
401
|
+
Use the `directory_path()` function when the user must provide or confirm a
|
|
402
|
+
directory path. It renders an interactive path-completion prompt restricted to
|
|
403
|
+
directories. In non-interactive sessions, the `default` value is returned
|
|
404
|
+
instead.
|
|
405
|
+
|
|
406
|
+
```python
|
|
407
|
+
dirpath = directory_path(
|
|
408
|
+
"Select the output directory:",
|
|
409
|
+
default="./output",
|
|
410
|
+
)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### CLI - Formatting, colors, and styles
|
|
224
414
|
|
|
225
415
|
Use these Rich markup colors/styles when formatting help text and messages.
|
|
226
416
|
These are the main colors/styles that can be used for highlighting/contrast (we
|
|
@@ -343,7 +533,7 @@ Here are some guidelines for when to use each formatting style.
|
|
|
343
533
|
The link provided is <https://github.com/nextmv-io/community-apps>, and it will
|
|
344
534
|
be applied to the text `nextmv-io/community-apps`.
|
|
345
535
|
|
|
346
|
-
|
|
536
|
+
#### CLI - Command documentation
|
|
347
537
|
|
|
348
538
|
Every command should have good-enough documentation that guides the user on how
|
|
349
539
|
to use it.
|
|
@@ -442,7 +632,7 @@ to use it.
|
|
|
442
632
|
--description "An application for routing hares"[/dim]
|
|
443
633
|
```
|
|
444
634
|
|
|
445
|
-
|
|
635
|
+
#### CLI - Command options
|
|
446
636
|
|
|
447
637
|
Consider the following guideline when declaring command options:
|
|
448
638
|
|
|
@@ -577,6 +767,9 @@ Consider the following guideline when declaring command options:
|
|
|
577
767
|
should always be the last option in the command's signature, for consistency
|
|
578
768
|
across the CLI.
|
|
579
769
|
|
|
770
|
+
[uv]: https://docs.astral.sh/uv/
|
|
771
|
+
[python]: https://www.python.org/downloads/
|
|
772
|
+
[nextpipe]: https://github.com/nextmv-io/nextpipe
|
|
580
773
|
[typer]: https://typer.tiangolo.com
|
|
581
774
|
[typer-learn]: https://typer.tiangolo.com/tutorial/
|
|
582
775
|
[rich]: https://rich.readthedocs.io/en/stable/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextmv
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0.dev1
|
|
4
4
|
Summary: The all-purpose Python SDK for Nextmv
|
|
5
5
|
Project-URL: Homepage, https://www.nextmv.io
|
|
6
6
|
Project-URL: Documentation, https://nextmv-py.docs.nextmv.io/en/latest/nextmv/
|
|
@@ -223,15 +223,10 @@ Requires-Dist: pip>=26.0
|
|
|
223
223
|
Requires-Dist: plotly>=6.0.1
|
|
224
224
|
Requires-Dist: pydantic>=2.5.2
|
|
225
225
|
Requires-Dist: pyyaml>=6.0.1
|
|
226
|
+
Requires-Dist: questionary>=2.1.1
|
|
226
227
|
Requires-Dist: requests>=2.31.0
|
|
227
228
|
Requires-Dist: typer>=0.20.1
|
|
228
229
|
Requires-Dist: urllib3>=2.1.0
|
|
229
|
-
Provides-Extra: all
|
|
230
|
-
Requires-Dist: folium>=0.20.0; extra == 'all'
|
|
231
|
-
Requires-Dist: mcp[cli]>=1.0; extra == 'all'
|
|
232
|
-
Requires-Dist: plotly>=6.0.1; extra == 'all'
|
|
233
|
-
Provides-Extra: mcp
|
|
234
|
-
Requires-Dist: mcp[cli]>=1.0; extra == 'mcp'
|
|
235
230
|
Provides-Extra: notebook
|
|
236
231
|
Requires-Dist: mlflow>=3.9.0; extra == 'notebook'
|
|
237
232
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v1.2.0.dev1"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from .__about__ import __version__
|
|
4
4
|
from .base_model import BaseModel as BaseModel
|
|
5
5
|
from .base_model import from_dict as from_dict
|
|
6
|
+
from .content_format import ContentFormat as ContentFormat
|
|
6
7
|
from .input import DataFile as DataFile
|
|
7
8
|
from .input import Input as Input
|
|
8
9
|
from .input import InputFormat as InputFormat
|
|
@@ -27,6 +28,7 @@ from .manifest import ManifestRuntime as ManifestRuntime
|
|
|
27
28
|
from .manifest import ManifestType as ManifestType
|
|
28
29
|
from .manifest import default_python_manifest as default_python_manifest
|
|
29
30
|
from .manifest import find_files as find_files
|
|
31
|
+
from .manifest import initialize_manifest as initialize_manifest
|
|
30
32
|
from .model import Model as Model
|
|
31
33
|
from .model import ModelConfiguration as ModelConfiguration
|
|
32
34
|
from .options import Option as Option
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AcceptanceTestIDOption, AppIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -47,7 +46,7 @@ def delete(
|
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
if not yes:
|
|
50
|
-
confirm =
|
|
49
|
+
confirm = confirmation(
|
|
51
50
|
f"Are you sure you want to delete acceptance test [magenta]{acceptance_test_id}[/magenta] "
|
|
52
51
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
53
52
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_account
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AccountIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -46,7 +45,7 @@ def delete(
|
|
|
46
45
|
"""
|
|
47
46
|
|
|
48
47
|
if not yes:
|
|
49
|
-
confirm =
|
|
48
|
+
confirm = confirmation(
|
|
50
49
|
f"Are you sure you want to delete account [magenta]{account_id}[/magenta]? This action cannot be undone.",
|
|
51
50
|
)
|
|
52
51
|
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -44,7 +43,7 @@ def delete(
|
|
|
44
43
|
"""
|
|
45
44
|
|
|
46
45
|
if not yes:
|
|
47
|
-
confirm =
|
|
46
|
+
confirm = confirmation(
|
|
48
47
|
f"Are you sure you want to delete application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
49
48
|
)
|
|
50
49
|
|
|
@@ -10,8 +10,7 @@ import typer
|
|
|
10
10
|
from rich.prompt import Prompt
|
|
11
11
|
|
|
12
12
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
13
|
-
from nextmv.cli.
|
|
14
|
-
from nextmv.cli.message import error, in_progress, info, success
|
|
13
|
+
from nextmv.cli.message import confirmation, error, in_progress, info, success
|
|
15
14
|
from nextmv.cli.options import AppIDOption, ProfileOption
|
|
16
15
|
from nextmv.cloud.application import Application
|
|
17
16
|
from nextmv.manifest import Manifest
|
|
@@ -256,7 +255,7 @@ def _handle_version_creation(
|
|
|
256
255
|
|
|
257
256
|
# If we are not auto-confirming version creation, ask the user.
|
|
258
257
|
if not version_yes:
|
|
259
|
-
should_create =
|
|
258
|
+
should_create = confirmation(
|
|
260
259
|
msg=f"Do you want to create a new version for application [magenta]{app_id}[/magenta] now?",
|
|
261
260
|
default=True,
|
|
262
261
|
)
|
|
@@ -327,7 +326,7 @@ def _handle_instance_prompting(
|
|
|
327
326
|
|
|
328
327
|
# If the instance exists, ask if we want to update it.
|
|
329
328
|
if exists:
|
|
330
|
-
should_update =
|
|
329
|
+
should_update = confirmation(
|
|
331
330
|
msg=f"Instance [magenta]{instance_id}[/magenta] exists. "
|
|
332
331
|
f"Do you want to link it to version [magenta]{version_id}[/magenta]?",
|
|
333
332
|
default=True,
|
|
@@ -347,7 +346,7 @@ def _handle_instance_prompting(
|
|
|
347
346
|
return
|
|
348
347
|
|
|
349
348
|
# If the instance does not exist, ask if we want to create it.
|
|
350
|
-
should_create =
|
|
349
|
+
should_create = confirmation(
|
|
351
350
|
msg=f"Instance [magenta]{instance_id}[/magenta] does not exist. "
|
|
352
351
|
f"Do you want to create it using version [magenta]{version_id}[/magenta]?",
|
|
353
352
|
default=True,
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, BatchExperimentIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -47,7 +46,7 @@ def delete(
|
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
if not yes:
|
|
50
|
-
confirm =
|
|
49
|
+
confirm = confirmation(
|
|
51
50
|
f"Are you sure you want to delete batch experiment [magenta]{batch_experiment_id}[/magenta] "
|
|
52
51
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
53
52
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, EnsembleDefinitionIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -46,7 +45,7 @@ def delete(
|
|
|
46
45
|
"""
|
|
47
46
|
|
|
48
47
|
if not yes:
|
|
49
|
-
confirm =
|
|
48
|
+
confirm = confirmation(
|
|
50
49
|
f"Are you sure you want to delete ensemble definition [magenta]{ensemble_definition_id}[/magenta] "
|
|
51
50
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
52
51
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, InputSetIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -47,7 +46,7 @@ def delete(
|
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
if not yes:
|
|
50
|
-
confirm =
|
|
49
|
+
confirm = confirmation(
|
|
51
50
|
f"Are you sure you want to delete input set [magenta]{input_set_id}[/magenta] "
|
|
52
51
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
53
52
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, InstanceIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -45,7 +44,7 @@ def delete(
|
|
|
45
44
|
"""
|
|
46
45
|
|
|
47
46
|
if not yes:
|
|
48
|
-
confirm =
|
|
47
|
+
confirm = confirmation(
|
|
49
48
|
f"Are you sure you want to delete instance [magenta]{instance_id}[/magenta] "
|
|
50
49
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
51
50
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, ManagedInputIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -47,7 +46,7 @@ def delete(
|
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
if not yes:
|
|
50
|
-
confirm =
|
|
49
|
+
confirm = confirmation(
|
|
51
50
|
f"Are you sure you want to delete managed input [magenta]{managed_input_id}[/magenta] "
|
|
52
51
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
53
52
|
)
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_marketplace_subscription
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import MarketplaceSubscriptionIDOption, ProfileOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -44,7 +43,7 @@ def delete(
|
|
|
44
43
|
"""
|
|
45
44
|
|
|
46
45
|
if not yes:
|
|
47
|
-
confirm =
|
|
46
|
+
confirm = confirmation(
|
|
48
47
|
f"Are you sure you want to delete subscription with ID [magenta]{subscription_id}[/magenta]?",
|
|
49
48
|
)
|
|
50
49
|
|
|
@@ -40,6 +40,17 @@ def create(
|
|
|
40
40
|
rich_help_panel="Input control",
|
|
41
41
|
),
|
|
42
42
|
] = None,
|
|
43
|
+
managed_input_id: Annotated[
|
|
44
|
+
str | None,
|
|
45
|
+
typer.Option(
|
|
46
|
+
"--managed-input-id",
|
|
47
|
+
"-m",
|
|
48
|
+
help="The Nextmv Cloud managed input ID to use as the input for the run.",
|
|
49
|
+
envvar="NEXTMV_MANAGED_INPUT_ID",
|
|
50
|
+
metavar="MANAGED_INPUT_ID",
|
|
51
|
+
rich_help_panel="Input control",
|
|
52
|
+
),
|
|
53
|
+
] = None,
|
|
43
54
|
# Options for controlling output.
|
|
44
55
|
logs: Annotated[
|
|
45
56
|
str | None,
|
|
@@ -209,9 +220,9 @@ def create(
|
|
|
209
220
|
"""
|
|
210
221
|
Create a new Nextmv Cloud application run.
|
|
211
222
|
|
|
212
|
-
Input for the run should be given through [magenta]stdin[/magenta]
|
|
213
|
-
|
|
214
|
-
following:
|
|
223
|
+
Input for the run should be given through [magenta]stdin[/magenta], the --input flag,
|
|
224
|
+
or a Nextmv managed input by passing its ID into the --managed-input-id flag.
|
|
225
|
+
When using the --input flag, the value can be one of the following:
|
|
215
226
|
|
|
216
227
|
- [yellow]<FILE_PATH>[/yellow]: path to a [magenta]file[/magenta] containing
|
|
217
228
|
the input data. Use with the [magenta]json[/magenta], and
|
|
@@ -297,12 +308,17 @@ def create(
|
|
|
297
308
|
submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]burrow[/magenta] instance.
|
|
298
309
|
Wait for the run to complete and download the result files to an [magenta]outputs[/magenta] directory.
|
|
299
310
|
$ [dim]nextmv cloud run create --app-id hare-app --input inputs --instance-id burrow --output outputs[/dim]
|
|
311
|
+
|
|
312
|
+
- Set the run to use a [magenta]Nextmv managed[/magenta] input with ID [magenta]carrot-input[/magenta],
|
|
313
|
+
and submit a run to an app with ID [magenta]hare-app[/magenta], using the [magenta]latest[/magenta] instance.
|
|
314
|
+
Wait for the run to complete and download the result files to an [magenta]outputs[/magenta] directory.
|
|
315
|
+
$ [dim]nextmv cloud run create --app-id hare-app --managed-input-id carrot-input --output outputs[/dim]
|
|
300
316
|
"""
|
|
301
317
|
|
|
302
318
|
# Validate that input is provided.
|
|
303
319
|
stdin = sys.stdin.read().strip() if sys.stdin.isatty() is False else None
|
|
304
|
-
if stdin is None and (input is None or input == ""):
|
|
305
|
-
error("Input data must be provided via the --input
|
|
320
|
+
if stdin is None and (input is None or input == "") and (managed_input_id is None or managed_input_id == ""):
|
|
321
|
+
error("Input data must be provided via the --input or --managed_input_id flags, or [magenta]stdin[/magenta].")
|
|
306
322
|
|
|
307
323
|
# Instantiate the basic requirements to start a new run.
|
|
308
324
|
cloud_app = build_cloud_app(app_id=app_id, profile=profile)
|
|
@@ -322,6 +338,7 @@ def create(
|
|
|
322
338
|
input_kwarg = resolve_input_kwarg(
|
|
323
339
|
stdin=stdin,
|
|
324
340
|
input=input,
|
|
341
|
+
managed_input_id=managed_input_id,
|
|
325
342
|
cloud_app=cloud_app,
|
|
326
343
|
)
|
|
327
344
|
run_id = cloud_app.new_run(
|
|
@@ -470,6 +487,7 @@ def build_run_options(options: list[str] | None) -> dict[str, str]:
|
|
|
470
487
|
def resolve_input_kwarg(
|
|
471
488
|
stdin: str | None,
|
|
472
489
|
input: str | None,
|
|
490
|
+
managed_input_id: str | None,
|
|
473
491
|
cloud_app: Application,
|
|
474
492
|
) -> dict[str, Any]:
|
|
475
493
|
"""
|
|
@@ -501,6 +519,9 @@ def resolve_input_kwarg(
|
|
|
501
519
|
|
|
502
520
|
return {"input": input_data}
|
|
503
521
|
|
|
522
|
+
if managed_input_id is not None and managed_input_id != "":
|
|
523
|
+
return {"managed_input_id": managed_input_id}
|
|
524
|
+
|
|
504
525
|
input_path = Path(input)
|
|
505
526
|
|
|
506
527
|
# If the input is a file, we need to determine if it is a tar file or
|
|
@@ -7,8 +7,7 @@ from typing import Annotated
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from nextmv.cli.configuration.config import build_cloud_app
|
|
10
|
-
from nextmv.cli.
|
|
11
|
-
from nextmv.cli.message import info, success
|
|
10
|
+
from nextmv.cli.message import confirmation, info, success
|
|
12
11
|
from nextmv.cli.options import AppIDOption, ProfileOption, ScenarioTestIDOption
|
|
13
12
|
|
|
14
13
|
# Set up subcommand application.
|
|
@@ -47,7 +46,7 @@ def delete(
|
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
if not yes:
|
|
50
|
-
confirm =
|
|
49
|
+
confirm = confirmation(
|
|
51
50
|
f"Are you sure you want to delete scenario test [magenta]{scenario_test_id}[/magenta] "
|
|
52
51
|
f"from application [magenta]{app_id}[/magenta]? This action cannot be undone.",
|
|
53
52
|
)
|