nextmv 1.0.0.dev4__tar.gz → 1.0.0.dev5__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.0.0.dev4 → nextmv-1.0.0.dev5}/PKG-INFO +1 -1
- nextmv-1.0.0.dev5/nextmv/__about__.py +1 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/__entrypoint__.py +1 -2
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/__init__.py +0 -4
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/_serialization.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/create.py +12 -12
- nextmv-1.0.0.dev5/nextmv/cli/cloud/app/push.py +451 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/confirm.py +5 -3
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/__init__.py +0 -38
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/acceptance_test.py +1 -65
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/account.py +1 -6
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/__init__.py +2 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_batch_scenario.py +2 -17
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_instance.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_managed_input.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_run.py +8 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_shadow.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_switchback.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_version.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/client.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/main.py +4 -6
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/deprecated.py +5 -3
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/input.py +0 -52
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/executor.py +83 -3
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/geojson_handler.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/runner.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/manifest.py +11 -7
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/model.py +2 -2
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/options.py +10 -255
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/output.py +57 -83
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/run.py +13 -13
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/status.py +1 -51
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/app.yaml +1 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_entrypoint/test_entrypoint.py +1 -1
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_manifest.py +1 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_options.py +1 -252
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_run.py +2 -13
- nextmv-1.0.0.dev4/nextmv/__about__.py +0 -1
- nextmv-1.0.0.dev4/nextmv/cli/cloud/app/push.py +0 -361
- nextmv-1.0.0.dev4/tests/scripts/options_deprecated.py +0 -8
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/.gitignore +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/LICENSE +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/README.md +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/base_model.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/CONTRIBUTING.md +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/acceptance/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/account/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/account/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/account/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/account/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/account/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/exists.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/app/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/metadata.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/batch/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/data/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/data/upload.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/ensemble/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/input_set/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/input_set/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/input_set/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/input_set/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/input_set/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/exists.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/instance/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/managed_input/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/cancel.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/input.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/logs.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/metadata.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/run/track.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/metadata.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/scenario/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/secrets/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/metadata.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/start.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/stop.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/shadow/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/metadata.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/start.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/stop.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/switchback/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/upload/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/upload/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/exists.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/get.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/cloud/version/update.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/community/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/community/clone.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/community/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/configuration/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/configuration/config.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/configuration/create.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/configuration/delete.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/configuration/list.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/main.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/message.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/options.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cli/version.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_acceptance.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_ensemble.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_input_set.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_secrets.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/application/_utils.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/assets.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/input_set.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/instance.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/integration.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/package.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/scenario.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/secrets.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/shadow.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/switchback.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/url.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/cloud/version.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/.gitignore +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/README.md +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/app.yaml +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/input.json +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/application.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/local.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/logger.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/polling.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/nextmv/safe.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/pyproject.toml +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cli/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cli/test_community.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cli/test_configuration.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cli/test_main.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cli/test_version.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/test_client.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/test_instance.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/test_package.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/cloud/test_scenario.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/local/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/local/test_application.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/local/test_executor.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/local/test_runner.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options1.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options2.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options3.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options4.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options5.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options6.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/scripts/options7.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_base_model.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_input.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_inputs/test_data.json +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_logger.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_model.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_output.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_polling.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_safe.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_serialization.py +0 -0
- {nextmv-1.0.0.dev4 → nextmv-1.0.0.dev5}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v1.0.0.dev5"
|
|
@@ -10,13 +10,12 @@ human to use it during local development. It is the standard way in which a
|
|
|
10
10
|
from mlflow.pyfunc import load_model
|
|
11
11
|
|
|
12
12
|
import nextmv
|
|
13
|
-
from nextmv import cloud
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def main() -> None:
|
|
17
16
|
"""Entry point for the program."""
|
|
18
17
|
|
|
19
|
-
manifest =
|
|
18
|
+
manifest = nextmv.Manifest.from_yaml(".")
|
|
20
19
|
|
|
21
20
|
# Load the options from the manifest.
|
|
22
21
|
options = manifest.extract_options()
|
|
@@ -11,7 +11,6 @@ from .input import LocalInputLoader as LocalInputLoader
|
|
|
11
11
|
from .input import csv_data_file as csv_data_file
|
|
12
12
|
from .input import json_data_file as json_data_file
|
|
13
13
|
from .input import load as load
|
|
14
|
-
from .input import load_local as load_local
|
|
15
14
|
from .input import text_data_file as text_data_file
|
|
16
15
|
from .logger import log as log
|
|
17
16
|
from .logger import redirect_stdout as redirect_stdout
|
|
@@ -31,7 +30,6 @@ from .model import Model as Model
|
|
|
31
30
|
from .model import ModelConfiguration as ModelConfiguration
|
|
32
31
|
from .options import Option as Option
|
|
33
32
|
from .options import Options as Options
|
|
34
|
-
from .options import Parameter as Parameter
|
|
35
33
|
from .output import Asset as Asset
|
|
36
34
|
from .output import DataPoint as DataPoint
|
|
37
35
|
from .output import LocalOutputWriter as LocalOutputWriter
|
|
@@ -50,7 +48,6 @@ from .output import csv_solution_file as csv_solution_file
|
|
|
50
48
|
from .output import json_solution_file as json_solution_file
|
|
51
49
|
from .output import text_solution_file as text_solution_file
|
|
52
50
|
from .output import write as write
|
|
53
|
-
from .output import write_local as write_local
|
|
54
51
|
from .polling import DEFAULT_POLLING_OPTIONS as DEFAULT_POLLING_OPTIONS
|
|
55
52
|
from .polling import PollingOptions as PollingOptions
|
|
56
53
|
from .polling import default_polling_options as default_polling_options
|
|
@@ -79,7 +76,6 @@ from .run import TrackedRunStatus as TrackedRunStatus
|
|
|
79
76
|
from .run import run_duration as run_duration
|
|
80
77
|
from .safe import safe_id as safe_id
|
|
81
78
|
from .safe import safe_name_and_id as safe_name_and_id
|
|
82
|
-
from .status import Status as Status
|
|
83
79
|
from .status import StatusV2 as StatusV2
|
|
84
80
|
|
|
85
81
|
VERSION = __version__
|
|
@@ -90,7 +90,7 @@ def _custom_serial(obj: Any) -> str:
|
|
|
90
90
|
If the object type is not supported for serialization.
|
|
91
91
|
"""
|
|
92
92
|
|
|
93
|
-
if isinstance(obj,
|
|
93
|
+
if isinstance(obj, datetime.datetime | datetime.date):
|
|
94
94
|
return obj.isoformat()
|
|
95
95
|
|
|
96
96
|
raise TypeError(f"Type {type(obj)} not serializable")
|
|
@@ -45,7 +45,7 @@ app = typer.Typer()
|
|
|
45
45
|
- Multiple metrics as a [magenta]json[/magenta] array in a single --metrics flag.
|
|
46
46
|
|
|
47
47
|
Each metric must have the following fields:
|
|
48
|
-
- [magenta]field[/magenta]: Field of the metric to measure (e.g., "
|
|
48
|
+
- [magenta]field[/magenta]: Field of the metric to measure (e.g., "result.custom.unassigned").
|
|
49
49
|
- [magenta]metric_type[/magenta]: Type of metric comparison. Allowed values: {enum_values(MetricType)}.
|
|
50
50
|
- [magenta]params[/magenta]: Parameters of the metric comparison.
|
|
51
51
|
- [magenta]operator[/magenta]: Comparison operator. Allowed values: {enum_values(Comparison)}.
|
|
@@ -71,8 +71,8 @@ app = typer.Typer()
|
|
|
71
71
|
[bold][underline]Examples[/underline][/bold]
|
|
72
72
|
|
|
73
73
|
- Create an acceptance test with a single metric.
|
|
74
|
-
$ [
|
|
75
|
-
"field": "
|
|
74
|
+
$ [green]METRIC='{{
|
|
75
|
+
"field": "result.custom.unassigned",
|
|
76
76
|
"metric_type": "direct-comparison",
|
|
77
77
|
"params": {{
|
|
78
78
|
"operator": "lt",
|
|
@@ -85,8 +85,8 @@ app = typer.Typer()
|
|
|
85
85
|
--metrics "$METRIC" --input-set-id input-set-123[/dim]
|
|
86
86
|
|
|
87
87
|
- Create with multiple metrics by repeating the flag.
|
|
88
|
-
$ [
|
|
89
|
-
"field": "
|
|
88
|
+
$ [green]METRIC1='{{
|
|
89
|
+
"field": "result.custom.unassigned",
|
|
90
90
|
"metric_type": "direct-comparison",
|
|
91
91
|
"params": {{
|
|
92
92
|
"operator": "lt",
|
|
@@ -95,7 +95,7 @@ app = typer.Typer()
|
|
|
95
95
|
"statistic": "mean"
|
|
96
96
|
}}'
|
|
97
97
|
METRIC2='{{
|
|
98
|
-
"field": "
|
|
98
|
+
"field": "run.duration",
|
|
99
99
|
"metric_type": "direct-comparison",
|
|
100
100
|
"params": {{
|
|
101
101
|
"operator": "le",
|
|
@@ -110,7 +110,7 @@ app = typer.Typer()
|
|
|
110
110
|
- Create with multiple metrics in a single [magenta]json[/magenta] array.
|
|
111
111
|
$ [dim]METRICS='[
|
|
112
112
|
{{
|
|
113
|
-
"field": "
|
|
113
|
+
"field": "result.custom.unassigned",
|
|
114
114
|
"metric_type": "direct-comparison",
|
|
115
115
|
"params": {{
|
|
116
116
|
"operator": "lt",
|
|
@@ -119,7 +119,7 @@ app = typer.Typer()
|
|
|
119
119
|
"statistic": "mean"
|
|
120
120
|
}},
|
|
121
121
|
{{
|
|
122
|
-
"field": "
|
|
122
|
+
"field": "run.duration",
|
|
123
123
|
"metric_type": "direct-comparison",
|
|
124
124
|
"params": {{
|
|
125
125
|
"operator": "le",
|
|
@@ -133,8 +133,8 @@ app = typer.Typer()
|
|
|
133
133
|
--metrics "$METRICS" --input-set-id input-set-123[/dim]
|
|
134
134
|
|
|
135
135
|
- Create an acceptance test and wait for it to complete.
|
|
136
|
-
$ [
|
|
137
|
-
"field": "
|
|
136
|
+
$ [green]METRIC='{{
|
|
137
|
+
"field": "result.custom.unassigned",
|
|
138
138
|
"metric_type": "direct-comparison",
|
|
139
139
|
"params": {{
|
|
140
140
|
"operator": "lt",
|
|
@@ -147,8 +147,8 @@ app = typer.Typer()
|
|
|
147
147
|
--metrics "$METRIC" --input-set-id input-set-123 --wait[/dim]
|
|
148
148
|
|
|
149
149
|
- Create an acceptance test and save the results to a file, waiting for completion.
|
|
150
|
-
$ [
|
|
151
|
-
"field": "
|
|
150
|
+
$ [green]METRIC='{{
|
|
151
|
+
"field": "result.custom.unassigned",
|
|
152
152
|
"metric_type": "direct-comparison",
|
|
153
153
|
"params": {{
|
|
154
154
|
"operator": "lt",
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the cloud app push command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.prompt import Prompt
|
|
11
|
+
|
|
12
|
+
from nextmv.cli.configuration.config import build_app
|
|
13
|
+
from nextmv.cli.confirm import get_confirmation
|
|
14
|
+
from nextmv.cli.message import error, in_progress, info, success
|
|
15
|
+
from nextmv.cli.options import AppIDOption, ProfileOption
|
|
16
|
+
from nextmv.cloud.application import Application
|
|
17
|
+
from nextmv.manifest import Manifest
|
|
18
|
+
|
|
19
|
+
# Set up subcommand application.
|
|
20
|
+
app = typer.Typer()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.command()
|
|
24
|
+
def push(
|
|
25
|
+
app_id: AppIDOption,
|
|
26
|
+
app_dir: Annotated[
|
|
27
|
+
str | None,
|
|
28
|
+
typer.Option(
|
|
29
|
+
"--app-dir",
|
|
30
|
+
"-d",
|
|
31
|
+
help="The path to the application's root directory.",
|
|
32
|
+
metavar="APP_DIR",
|
|
33
|
+
),
|
|
34
|
+
] = ".",
|
|
35
|
+
manifest: Annotated[
|
|
36
|
+
str | None,
|
|
37
|
+
typer.Option(
|
|
38
|
+
"--manifest",
|
|
39
|
+
"-m",
|
|
40
|
+
help="Path to the application manifest file ([magenta]app.yaml[/magenta]).",
|
|
41
|
+
metavar="MANIFEST_PATH",
|
|
42
|
+
),
|
|
43
|
+
] = None,
|
|
44
|
+
# Options for version control.
|
|
45
|
+
version_id: Annotated[
|
|
46
|
+
str | None,
|
|
47
|
+
typer.Option(
|
|
48
|
+
"--version-id",
|
|
49
|
+
"-v",
|
|
50
|
+
help="Custom ID for version creation after app push. Automatically generated if not provided. "
|
|
51
|
+
"Activates --version-yes.",
|
|
52
|
+
metavar="VERSION_ID",
|
|
53
|
+
rich_help_panel="Version control",
|
|
54
|
+
),
|
|
55
|
+
] = None,
|
|
56
|
+
version_yes: Annotated[
|
|
57
|
+
bool,
|
|
58
|
+
typer.Option(
|
|
59
|
+
"--version-yes",
|
|
60
|
+
"-y",
|
|
61
|
+
help="Create a new version after push. Skips confirmation prompt. Useful for non-interactive sessions.",
|
|
62
|
+
rich_help_panel="Version control",
|
|
63
|
+
),
|
|
64
|
+
] = False,
|
|
65
|
+
# Options for instance control.
|
|
66
|
+
create_instance_id: Annotated[
|
|
67
|
+
str | None,
|
|
68
|
+
typer.Option(
|
|
69
|
+
"--create-instance-id",
|
|
70
|
+
"-c",
|
|
71
|
+
help="Link the newly created version to a [yellow]new[/yellow] instance with this ID. "
|
|
72
|
+
"Skips prompt to provide an instance ID. Useful for non-interactive sessions.",
|
|
73
|
+
metavar="CREATE_INSTANCE_ID",
|
|
74
|
+
rich_help_panel="Instance control",
|
|
75
|
+
),
|
|
76
|
+
] = None,
|
|
77
|
+
update_instance_id: Annotated[
|
|
78
|
+
str | None,
|
|
79
|
+
typer.Option(
|
|
80
|
+
"--update-instance-id",
|
|
81
|
+
"-u",
|
|
82
|
+
help="Link the newly created version to an [yellow]existing[/yellow] instance with this ID. "
|
|
83
|
+
"Skips prompt to provide an instance ID. Useful for non-interactive sessions.",
|
|
84
|
+
metavar="UPDATE_INSTANCE_ID",
|
|
85
|
+
rich_help_panel="Instance control",
|
|
86
|
+
),
|
|
87
|
+
] = None,
|
|
88
|
+
profile: ProfileOption = None,
|
|
89
|
+
) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Push (deploy) a Nextmv application to Nextmv Cloud.
|
|
92
|
+
|
|
93
|
+
Use the --app-dir option to specify the path to your application's root
|
|
94
|
+
directory. By default, the current working directory is used.
|
|
95
|
+
|
|
96
|
+
You can also provide a custom manifest file using the --manifest option. If
|
|
97
|
+
not provided, the CLI will look for a file named
|
|
98
|
+
[magenta]app.yaml[/magenta] in the application's root.
|
|
99
|
+
|
|
100
|
+
By default, this command only pushes the app. After the push, you will be
|
|
101
|
+
prompted to create a new version. If a new version is created, you will be
|
|
102
|
+
prompted to link it to an instance. If the instance exists, you will be
|
|
103
|
+
asked if you want to update it. If it doesn't, you will be asked to create
|
|
104
|
+
it. You can use the following flags to skip the prompts, useful in
|
|
105
|
+
non-interactive sessions like in a CI/CD pipeline: --version-yes,
|
|
106
|
+
--version-id, --create-instance-id, and --update-instance-id.
|
|
107
|
+
|
|
108
|
+
[bold][underline]Examples[/underline][/bold]
|
|
109
|
+
|
|
110
|
+
- Push an application, with ID [magenta]hare-app[/magenta], from the current directory.
|
|
111
|
+
$ [dim]nextmv cloud app push --app-id hare-app[/dim]
|
|
112
|
+
|
|
113
|
+
- Push an application, with ID [magenta]hare-app[/magenta], from the [magenta]./my-app[/magenta] directory.
|
|
114
|
+
$ [dim]nextmv cloud app push --app-id hare-app --app-dir ./my-app[/dim]
|
|
115
|
+
|
|
116
|
+
- Push an application, with ID [magenta]hare-app[/magenta], using a custom manifest file.
|
|
117
|
+
$ [dim]nextmv cloud app push --app-id hare-app --manifest ./custom-manifest.yaml[/dim]
|
|
118
|
+
|
|
119
|
+
- Push and automatically create a new version (no prompt).
|
|
120
|
+
$ [dim]nextmv cloud app push --app-id hare-app --version-yes[/dim]
|
|
121
|
+
|
|
122
|
+
- Push and create a new version with a custom version ID (no prompt).
|
|
123
|
+
$ [dim]nextmv cloud app push --app-id hare-app --version-id v1.0.0[/dim]
|
|
124
|
+
|
|
125
|
+
- Push and create a new version, then link it to a new instance with a specific ID (no prompt).
|
|
126
|
+
$ [dim]nextmv cloud app push --app-id hare-app --version-yes --create-instance-id inst-1[/dim]
|
|
127
|
+
|
|
128
|
+
- Push and create a new version, then link it to an existing instance (no prompt).
|
|
129
|
+
$ [dim]nextmv cloud app push --app-id hare-app --version-yes --update-instance-id inst-1[/dim]
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
# We cannot create and update an instance at the same time.
|
|
133
|
+
update_defined = update_instance_id is not None and update_instance_id != ""
|
|
134
|
+
create_defined = create_instance_id is not None and create_instance_id != ""
|
|
135
|
+
if update_defined and create_defined:
|
|
136
|
+
error("Cannot use --update-instance-id and --create-instance-id at the same time.")
|
|
137
|
+
|
|
138
|
+
cloud_app = build_app(app_id=app_id, profile=profile)
|
|
139
|
+
|
|
140
|
+
# We cannot update an instance that does not exist.
|
|
141
|
+
if update_defined and not cloud_app.instance_exists(instance_id=update_instance_id):
|
|
142
|
+
error(
|
|
143
|
+
f"Used option --update-instance-id but the instance {update_instance_id} does not exist. "
|
|
144
|
+
"Use --create-instance-id instead."
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# We cannot create an instance that already exists.
|
|
148
|
+
if create_defined and cloud_app.instance_exists(instance_id=create_instance_id):
|
|
149
|
+
error(
|
|
150
|
+
f"Used option --create-instance-id but the instance {create_instance_id} already exists. "
|
|
151
|
+
"Use --update-instance-id instead."
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Do the normal push first.
|
|
155
|
+
loaded_manifest = Manifest.from_yaml(dirpath=manifest) if manifest is not None and manifest != "" else None
|
|
156
|
+
cloud_app.push(
|
|
157
|
+
manifest=loaded_manifest,
|
|
158
|
+
app_dir=app_dir,
|
|
159
|
+
verbose=True,
|
|
160
|
+
rich_print=True,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
now = datetime.now(timezone.utc)
|
|
164
|
+
version_id, should_continue = _handle_version_creation(
|
|
165
|
+
cloud_app=cloud_app,
|
|
166
|
+
app_id=app_id,
|
|
167
|
+
version_id=version_id,
|
|
168
|
+
version_yes=version_yes,
|
|
169
|
+
now=now,
|
|
170
|
+
)
|
|
171
|
+
if not should_continue:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# If the override for updating an instance was used, we update the instance
|
|
175
|
+
# and we are done.
|
|
176
|
+
if update_defined:
|
|
177
|
+
info("Used option --update-instance-id to link version to existing instance.", emoji=":bulb:")
|
|
178
|
+
_update_instance(
|
|
179
|
+
cloud_app=cloud_app,
|
|
180
|
+
app_id=app_id,
|
|
181
|
+
version_id=version_id,
|
|
182
|
+
instance_id=update_instance_id,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
# If the override for creating a new instance was used, we create the
|
|
188
|
+
# instance and we are done.
|
|
189
|
+
if create_defined:
|
|
190
|
+
info("Used option --create-instance-id to link version to new instance.", emoji=":bulb:")
|
|
191
|
+
_create_instance(
|
|
192
|
+
cloud_app=cloud_app,
|
|
193
|
+
app_id=app_id,
|
|
194
|
+
version_id=version_id,
|
|
195
|
+
instance_id=create_instance_id,
|
|
196
|
+
now=now,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# If no overrides are used, we handle instance prompting.
|
|
202
|
+
_handle_instance_prompting(
|
|
203
|
+
cloud_app=cloud_app,
|
|
204
|
+
app_id=app_id,
|
|
205
|
+
version_id=version_id,
|
|
206
|
+
now=now,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _handle_version_creation(
|
|
211
|
+
cloud_app: Application,
|
|
212
|
+
app_id: str,
|
|
213
|
+
version_id: str | None,
|
|
214
|
+
version_yes: bool,
|
|
215
|
+
now: datetime,
|
|
216
|
+
) -> tuple[str, bool]:
|
|
217
|
+
"""
|
|
218
|
+
Handle the logic for version creation after pushing an application.
|
|
219
|
+
|
|
220
|
+
If a version ID is provided and exists, it is used directly. If not, the user is prompted (unless auto-confirmed)
|
|
221
|
+
to create a new version. If confirmed, a new version is created with an automatic description.
|
|
222
|
+
|
|
223
|
+
Parameters
|
|
224
|
+
----------
|
|
225
|
+
cloud_app : Application
|
|
226
|
+
The cloud application object to interact with Nextmv Cloud.
|
|
227
|
+
app_id : str
|
|
228
|
+
The application ID.
|
|
229
|
+
version_id : str or None
|
|
230
|
+
The version ID to use or check for existence. If None or empty, a new version may be created.
|
|
231
|
+
version_yes : bool
|
|
232
|
+
Whether to skip the prompt and auto-create a new version.
|
|
233
|
+
now : datetime
|
|
234
|
+
The current datetime, used for version description.
|
|
235
|
+
|
|
236
|
+
Returns
|
|
237
|
+
-------
|
|
238
|
+
tuple[str, bool]
|
|
239
|
+
A tuple containing the version ID (empty string if not created) and a boolean indicating
|
|
240
|
+
whether to continue with subsequent steps (True if a version is selected or created, False otherwise).
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
# If the user provides a version, and it exists, we use it directly and we
|
|
244
|
+
# are done.
|
|
245
|
+
if version_id is not None and version_id != "":
|
|
246
|
+
exists = cloud_app.version_exists(version_id=version_id)
|
|
247
|
+
if exists:
|
|
248
|
+
error(
|
|
249
|
+
f"Version [magenta]{version_id}[/magenta] already exists for application [magenta]{app_id}[/magenta]."
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
return "", False
|
|
253
|
+
|
|
254
|
+
info(
|
|
255
|
+
msg=f"Version [magenta]{version_id}[/magenta] does not exist. A new version will be created.",
|
|
256
|
+
emoji=":bulb:",
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
version_yes = True # Activate auto-confirm since user provided a version ID.
|
|
260
|
+
|
|
261
|
+
# If we are not auto-confirming version creation, ask the user.
|
|
262
|
+
if not version_yes:
|
|
263
|
+
should_create = get_confirmation(
|
|
264
|
+
msg=f"Do you want to create a new version [magenta]{app_id}[/magenta] now?",
|
|
265
|
+
default=True,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# If the user does not want to create a new version, we are done.
|
|
269
|
+
if not should_create:
|
|
270
|
+
info(
|
|
271
|
+
msg="Will not create a new version.",
|
|
272
|
+
emoji=":bulb:",
|
|
273
|
+
)
|
|
274
|
+
return "", False
|
|
275
|
+
|
|
276
|
+
# Create a new version if either the user confirms by prompt or by using
|
|
277
|
+
# the flag.
|
|
278
|
+
in_progress("Creating a new version...")
|
|
279
|
+
version_description = f"Version created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
|
280
|
+
version = cloud_app.new_version(
|
|
281
|
+
id=version_id,
|
|
282
|
+
description=version_description,
|
|
283
|
+
)
|
|
284
|
+
version_id = version.id
|
|
285
|
+
success(f"New version [magenta]{version_id}[/magenta] created for application [magenta]{app_id}[/magenta].")
|
|
286
|
+
|
|
287
|
+
return version_id, True
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _handle_instance_prompting(
|
|
291
|
+
cloud_app: Application,
|
|
292
|
+
app_id: str,
|
|
293
|
+
version_id: str,
|
|
294
|
+
now: datetime,
|
|
295
|
+
) -> None:
|
|
296
|
+
"""
|
|
297
|
+
Handle interactive prompting for linking a version to an instance after a push.
|
|
298
|
+
|
|
299
|
+
In interactive terminals, prompts the user to link the new version to an existing or new instance.
|
|
300
|
+
If the terminal is non-interactive, skips prompting. Handles both updating existing instances and creating new ones.
|
|
301
|
+
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
cloud_app : Application
|
|
305
|
+
The cloud application object to interact with Nextmv Cloud.
|
|
306
|
+
app_id : str
|
|
307
|
+
The application ID.
|
|
308
|
+
version_id : str
|
|
309
|
+
The version ID to link to an instance.
|
|
310
|
+
now : datetime
|
|
311
|
+
The current datetime, used for instance description if a new instance is created.
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
# If this is not an interactive terminal, do not ask for instance linking,
|
|
315
|
+
# to avoid hanging indefinitely waiting for a user response.
|
|
316
|
+
if not sys.stdin.isatty():
|
|
317
|
+
info(
|
|
318
|
+
msg="Non-interactive terminal detected. Skipping instance linking.",
|
|
319
|
+
emoji=":bulb:",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
return
|
|
323
|
+
|
|
324
|
+
# Prompt the user for an instance ID to link the new version to.
|
|
325
|
+
instance_id = Prompt.ask(
|
|
326
|
+
f"Do you want to link version [magenta]{version_id}[/magenta] to an instance? If so, enter the instance ID. "
|
|
327
|
+
"Leave blank to abort",
|
|
328
|
+
case_sensitive=False,
|
|
329
|
+
)
|
|
330
|
+
if instance_id == "":
|
|
331
|
+
info(
|
|
332
|
+
msg="No instance ID provided. Skipping instance linking.",
|
|
333
|
+
emoji=":bulb:",
|
|
334
|
+
)
|
|
335
|
+
return
|
|
336
|
+
|
|
337
|
+
# Based on whether the instance exists or not, ask the user if they want to
|
|
338
|
+
# update or create it.
|
|
339
|
+
exists = cloud_app.instance_exists(instance_id=instance_id)
|
|
340
|
+
|
|
341
|
+
# If the instance exists, ask if we want to update it.
|
|
342
|
+
if exists:
|
|
343
|
+
should_update = get_confirmation(
|
|
344
|
+
msg=f"Instance [magenta]{instance_id}[/magenta] exists. "
|
|
345
|
+
f"Do you want to link it to version [magenta]{version_id}[/magenta]?",
|
|
346
|
+
default=True,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
if not should_update:
|
|
350
|
+
info(
|
|
351
|
+
msg=f"Will not update instance [magenta]{instance_id}[/magenta].",
|
|
352
|
+
emoji=":bulb:",
|
|
353
|
+
)
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
_update_instance(
|
|
357
|
+
cloud_app=cloud_app,
|
|
358
|
+
app_id=app_id,
|
|
359
|
+
version_id=version_id,
|
|
360
|
+
instance_id=instance_id,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
# If the instance does not exist, ask if we want to create it.
|
|
366
|
+
should_create = get_confirmation(
|
|
367
|
+
msg=f"Instance [magenta]{instance_id}[/magenta] does not exist. "
|
|
368
|
+
f"Do you want to create it using version [magenta]{version_id}[/magenta]?",
|
|
369
|
+
default=True,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
if not should_create:
|
|
373
|
+
info(
|
|
374
|
+
msg=f"Will not create instance [magenta]{instance_id}[/magenta].",
|
|
375
|
+
emoji=":bulb:",
|
|
376
|
+
)
|
|
377
|
+
return
|
|
378
|
+
|
|
379
|
+
_create_instance(
|
|
380
|
+
cloud_app=cloud_app,
|
|
381
|
+
app_id=app_id,
|
|
382
|
+
version_id=version_id,
|
|
383
|
+
instance_id=instance_id,
|
|
384
|
+
now=now,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _update_instance(
|
|
389
|
+
cloud_app: Application,
|
|
390
|
+
app_id: str,
|
|
391
|
+
version_id: str,
|
|
392
|
+
instance_id: str,
|
|
393
|
+
) -> None:
|
|
394
|
+
"""
|
|
395
|
+
Update an existing instance to use a new version.
|
|
396
|
+
|
|
397
|
+
Parameters
|
|
398
|
+
----------
|
|
399
|
+
cloud_app : Application
|
|
400
|
+
The cloud application object to interact with Nextmv Cloud.
|
|
401
|
+
app_id : str
|
|
402
|
+
The application ID.
|
|
403
|
+
version_id : str
|
|
404
|
+
The version ID to link to the instance.
|
|
405
|
+
instance_id : str
|
|
406
|
+
The instance ID to update.
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
in_progress(f"Updating instance [magenta]{instance_id}[/magenta] to use version [magenta]{version_id}[/magenta]...")
|
|
410
|
+
cloud_app.update_instance(id=instance_id, version_id=version_id)
|
|
411
|
+
success(
|
|
412
|
+
f"Instance [magenta]{instance_id}[/magenta] updated to use version [magenta]{version_id}[/magenta] "
|
|
413
|
+
f"for application [magenta]{app_id}[/magenta]."
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def _create_instance(
|
|
418
|
+
cloud_app: Application,
|
|
419
|
+
app_id: str,
|
|
420
|
+
version_id: str,
|
|
421
|
+
instance_id: str,
|
|
422
|
+
now: datetime,
|
|
423
|
+
) -> None:
|
|
424
|
+
"""
|
|
425
|
+
Create a new instance linked to a specific version.
|
|
426
|
+
|
|
427
|
+
Parameters
|
|
428
|
+
----------
|
|
429
|
+
cloud_app : Application
|
|
430
|
+
The cloud application object to interact with Nextmv Cloud.
|
|
431
|
+
app_id : str
|
|
432
|
+
The application ID.
|
|
433
|
+
version_id : str
|
|
434
|
+
The version ID to link to the new instance.
|
|
435
|
+
instance_id : str
|
|
436
|
+
The instance ID to create.
|
|
437
|
+
now : datetime
|
|
438
|
+
The current datetime, used for the instance description.
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
in_progress(f"Creating a new instance with ID [magenta]{instance_id}[/magenta]...")
|
|
442
|
+
instance_description = f"Instance created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
|
443
|
+
instance = cloud_app.new_instance(
|
|
444
|
+
version_id=version_id,
|
|
445
|
+
id=instance_id,
|
|
446
|
+
description=instance_description,
|
|
447
|
+
)
|
|
448
|
+
success(
|
|
449
|
+
f"New instance [magenta]{instance.id}[/magenta] created using version [magenta]{version_id}[/magenta] "
|
|
450
|
+
f"for application [magenta]{app_id}[/magenta]."
|
|
451
|
+
)
|
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
from rich.prompt import Confirm
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def get_confirmation(msg: str) -> bool:
|
|
6
|
+
def get_confirmation(msg: str, default: bool = False) -> bool:
|
|
7
7
|
"""
|
|
8
8
|
Method to get a yes/no confirmation from the user.
|
|
9
9
|
|
|
@@ -11,6 +11,8 @@ def get_confirmation(msg: str) -> bool:
|
|
|
11
11
|
----------
|
|
12
12
|
msg : str
|
|
13
13
|
The message to display to the user.
|
|
14
|
+
default : bool, optional
|
|
15
|
+
The default value if the user just presses Enter. Default is False.
|
|
14
16
|
|
|
15
17
|
Returns
|
|
16
18
|
-------
|
|
@@ -21,11 +23,11 @@ def get_confirmation(msg: str) -> bool:
|
|
|
21
23
|
# If this is not an interactive terminal, do not ask for confirmation, to
|
|
22
24
|
# avoid hanging indefinitely waiting for a user response.
|
|
23
25
|
if not sys.stdin.isatty():
|
|
24
|
-
return
|
|
26
|
+
return default
|
|
25
27
|
|
|
26
28
|
return Confirm.ask(
|
|
27
29
|
msg,
|
|
28
|
-
default=
|
|
30
|
+
default=default,
|
|
29
31
|
case_sensitive=False,
|
|
30
32
|
show_default=True,
|
|
31
33
|
show_choices=True,
|
|
@@ -1,42 +1,5 @@
|
|
|
1
1
|
"""Functionality for interacting with the Nextmv Cloud."""
|
|
2
2
|
|
|
3
|
-
# These imports are kept for backwards compatibility but the preferred import path is
|
|
4
|
-
# from nextmv directly. These imports will be removed in a future release.
|
|
5
|
-
from nextmv.manifest import MANIFEST_FILE_NAME as MANIFEST_FILE_NAME
|
|
6
|
-
from nextmv.manifest import Manifest as Manifest
|
|
7
|
-
from nextmv.manifest import ManifestBuild as ManifestBuild
|
|
8
|
-
from nextmv.manifest import ManifestContent as ManifestContent
|
|
9
|
-
from nextmv.manifest import ManifestContentMultiFile as ManifestContentMultiFile
|
|
10
|
-
from nextmv.manifest import ManifestContentMultiFileInput as ManifestContentMultiFileInput
|
|
11
|
-
from nextmv.manifest import ManifestContentMultiFileOutput as ManifestContentMultiFileOutput
|
|
12
|
-
from nextmv.manifest import ManifestOption as ManifestOption
|
|
13
|
-
from nextmv.manifest import ManifestPython as ManifestPython
|
|
14
|
-
from nextmv.manifest import ManifestPythonModel as ManifestPythonModel
|
|
15
|
-
from nextmv.manifest import ManifestRuntime as ManifestRuntime
|
|
16
|
-
from nextmv.manifest import ManifestType as ManifestType
|
|
17
|
-
from nextmv.polling import PollingOptions as PollingOptions
|
|
18
|
-
from nextmv.polling import poll as poll
|
|
19
|
-
from nextmv.run import ErrorLog as ErrorLog
|
|
20
|
-
from nextmv.run import ExternalRunResult as ExternalRunResult
|
|
21
|
-
from nextmv.run import Format as Format
|
|
22
|
-
from nextmv.run import FormatInput as FormatInput
|
|
23
|
-
from nextmv.run import FormatOutput as FormatOutput
|
|
24
|
-
from nextmv.run import Metadata as Metadata
|
|
25
|
-
from nextmv.run import RunConfiguration as RunConfiguration
|
|
26
|
-
from nextmv.run import RunInformation as RunInformation
|
|
27
|
-
from nextmv.run import RunLog as RunLog
|
|
28
|
-
from nextmv.run import RunQueuing as RunQueuing
|
|
29
|
-
from nextmv.run import RunResult as RunResult
|
|
30
|
-
from nextmv.run import RunType as RunType
|
|
31
|
-
from nextmv.run import RunTypeConfiguration as RunTypeConfiguration
|
|
32
|
-
from nextmv.run import TrackedRun as TrackedRun
|
|
33
|
-
from nextmv.run import TrackedRunStatus as TrackedRunStatus
|
|
34
|
-
from nextmv.run import run_duration as run_duration
|
|
35
|
-
from nextmv.safe import safe_id as safe_id
|
|
36
|
-
from nextmv.safe import safe_name_and_id as safe_name_and_id
|
|
37
|
-
from nextmv.status import Status as Status
|
|
38
|
-
from nextmv.status import StatusV2 as StatusV2
|
|
39
|
-
|
|
40
3
|
from .acceptance_test import AcceptanceTest as AcceptanceTest
|
|
41
4
|
from .acceptance_test import AcceptanceTestResults as AcceptanceTestResults
|
|
42
5
|
from .acceptance_test import Comparison as Comparison
|
|
@@ -52,7 +15,6 @@ from .acceptance_test import MetricToleranceType as MetricToleranceType
|
|
|
52
15
|
from .acceptance_test import MetricType as MetricType
|
|
53
16
|
from .acceptance_test import ResultStatistics as ResultStatistics
|
|
54
17
|
from .acceptance_test import StatisticType as StatisticType
|
|
55
|
-
from .acceptance_test import ToleranceType as ToleranceType
|
|
56
18
|
from .account import Account as Account
|
|
57
19
|
from .account import AccountMember as AccountMember
|
|
58
20
|
from .account import Queue as Queue
|