nextmv 1.0.0.dev2__py3-none-any.whl → 1.0.0.dev4__py3-none-any.whl

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.
Files changed (120) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/cli/CONTRIBUTING.md +81 -29
  3. nextmv/cli/cloud/__init__.py +2 -0
  4. nextmv/cli/cloud/acceptance/create.py +20 -22
  5. nextmv/cli/cloud/acceptance/delete.py +7 -8
  6. nextmv/cli/cloud/acceptance/get.py +9 -10
  7. nextmv/cli/cloud/acceptance/list.py +3 -3
  8. nextmv/cli/cloud/acceptance/update.py +6 -6
  9. nextmv/cli/cloud/account/__init__.py +3 -3
  10. nextmv/cli/cloud/account/create.py +11 -11
  11. nextmv/cli/cloud/account/delete.py +6 -7
  12. nextmv/cli/cloud/account/get.py +3 -3
  13. nextmv/cli/cloud/account/update.py +5 -5
  14. nextmv/cli/cloud/app/create.py +25 -26
  15. nextmv/cli/cloud/app/delete.py +5 -6
  16. nextmv/cli/cloud/app/exists.py +2 -2
  17. nextmv/cli/cloud/app/get.py +2 -2
  18. nextmv/cli/cloud/app/list.py +3 -3
  19. nextmv/cli/cloud/app/push.py +269 -45
  20. nextmv/cli/cloud/app/update.py +12 -12
  21. nextmv/cli/cloud/batch/create.py +26 -28
  22. nextmv/cli/cloud/batch/delete.py +5 -6
  23. nextmv/cli/cloud/batch/get.py +8 -8
  24. nextmv/cli/cloud/batch/list.py +3 -3
  25. nextmv/cli/cloud/batch/metadata.py +4 -4
  26. nextmv/cli/cloud/batch/update.py +6 -6
  27. nextmv/cli/cloud/data/__init__.py +1 -1
  28. nextmv/cli/cloud/data/upload.py +15 -15
  29. nextmv/cli/cloud/ensemble/__init__.py +2 -0
  30. nextmv/cli/cloud/ensemble/create.py +21 -22
  31. nextmv/cli/cloud/ensemble/delete.py +5 -6
  32. nextmv/cli/cloud/ensemble/get.py +4 -4
  33. nextmv/cli/cloud/ensemble/list.py +63 -0
  34. nextmv/cli/cloud/ensemble/update.py +9 -9
  35. nextmv/cli/cloud/input_set/create.py +20 -22
  36. nextmv/cli/cloud/input_set/get.py +3 -3
  37. nextmv/cli/cloud/input_set/list.py +3 -3
  38. nextmv/cli/cloud/input_set/update.py +24 -24
  39. nextmv/cli/cloud/instance/create.py +14 -15
  40. nextmv/cli/cloud/instance/delete.py +5 -6
  41. nextmv/cli/cloud/instance/exists.py +2 -2
  42. nextmv/cli/cloud/instance/get.py +2 -2
  43. nextmv/cli/cloud/instance/list.py +3 -3
  44. nextmv/cli/cloud/instance/update.py +14 -14
  45. nextmv/cli/cloud/managed_input/create.py +14 -16
  46. nextmv/cli/cloud/managed_input/delete.py +6 -7
  47. nextmv/cli/cloud/managed_input/get.py +3 -3
  48. nextmv/cli/cloud/managed_input/list.py +3 -3
  49. nextmv/cli/cloud/managed_input/update.py +9 -9
  50. nextmv/cli/cloud/run/cancel.py +2 -2
  51. nextmv/cli/cloud/run/create.py +32 -33
  52. nextmv/cli/cloud/run/get.py +8 -8
  53. nextmv/cli/cloud/run/input.py +4 -4
  54. nextmv/cli/cloud/run/list.py +6 -6
  55. nextmv/cli/cloud/run/logs.py +9 -10
  56. nextmv/cli/cloud/run/metadata.py +4 -4
  57. nextmv/cli/cloud/run/track.py +32 -33
  58. nextmv/cli/cloud/scenario/create.py +21 -21
  59. nextmv/cli/cloud/scenario/delete.py +5 -6
  60. nextmv/cli/cloud/scenario/get.py +8 -8
  61. nextmv/cli/cloud/scenario/list.py +3 -3
  62. nextmv/cli/cloud/scenario/metadata.py +4 -4
  63. nextmv/cli/cloud/scenario/update.py +6 -6
  64. nextmv/cli/cloud/secrets/create.py +17 -17
  65. nextmv/cli/cloud/secrets/delete.py +5 -6
  66. nextmv/cli/cloud/secrets/get.py +4 -4
  67. nextmv/cli/cloud/secrets/list.py +3 -3
  68. nextmv/cli/cloud/secrets/update.py +17 -20
  69. nextmv/cli/cloud/shadow/__init__.py +1 -1
  70. nextmv/cli/cloud/shadow/create.py +32 -32
  71. nextmv/cli/cloud/shadow/delete.py +5 -6
  72. nextmv/cli/cloud/shadow/get.py +2 -2
  73. nextmv/cli/cloud/shadow/list.py +3 -3
  74. nextmv/cli/cloud/shadow/metadata.py +4 -4
  75. nextmv/cli/cloud/shadow/start.py +3 -3
  76. nextmv/cli/cloud/shadow/stop.py +8 -10
  77. nextmv/cli/cloud/shadow/update.py +7 -6
  78. nextmv/cli/cloud/switchback/__init__.py +33 -0
  79. nextmv/cli/cloud/switchback/create.py +151 -0
  80. nextmv/cli/cloud/switchback/delete.py +67 -0
  81. nextmv/cli/cloud/switchback/get.py +62 -0
  82. nextmv/cli/cloud/switchback/list.py +63 -0
  83. nextmv/cli/cloud/switchback/metadata.py +68 -0
  84. nextmv/cli/cloud/switchback/start.py +43 -0
  85. nextmv/cli/cloud/switchback/stop.py +41 -0
  86. nextmv/cli/cloud/switchback/update.py +96 -0
  87. nextmv/cli/cloud/upload/create.py +2 -2
  88. nextmv/cli/cloud/version/create.py +9 -10
  89. nextmv/cli/cloud/version/delete.py +5 -6
  90. nextmv/cli/cloud/version/exists.py +2 -2
  91. nextmv/cli/cloud/version/get.py +2 -2
  92. nextmv/cli/cloud/version/list.py +3 -3
  93. nextmv/cli/cloud/version/update.py +8 -8
  94. nextmv/cli/community/clone.py +12 -10
  95. nextmv/cli/community/list.py +9 -9
  96. nextmv/cli/configuration/config.py +43 -10
  97. nextmv/cli/configuration/create.py +3 -3
  98. nextmv/cli/configuration/delete.py +7 -7
  99. nextmv/cli/configuration/list.py +3 -3
  100. nextmv/cli/confirm.py +32 -0
  101. nextmv/cli/main.py +27 -36
  102. nextmv/cli/message.py +2 -2
  103. nextmv/cli/options.py +14 -0
  104. nextmv/cli/version.py +1 -1
  105. nextmv/cloud/__init__.py +5 -0
  106. nextmv/cloud/application/__init__.py +192 -54
  107. nextmv/cloud/application/_batch_scenario.py +2 -2
  108. nextmv/cloud/application/_instance.py +2 -2
  109. nextmv/cloud/application/_managed_input.py +1 -1
  110. nextmv/cloud/application/_shadow.py +1 -1
  111. nextmv/cloud/application/_switchback.py +323 -0
  112. nextmv/cloud/application/_version.py +3 -2
  113. nextmv/cloud/shadow.py +43 -4
  114. nextmv/cloud/switchback.py +226 -0
  115. {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev4.dist-info}/METADATA +1 -1
  116. nextmv-1.0.0.dev4.dist-info/RECORD +183 -0
  117. nextmv-1.0.0.dev2.dist-info/RECORD +0 -170
  118. {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev4.dist-info}/WHEEL +0 -0
  119. {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev4.dist-info}/entry_points.txt +0 -0
  120. {nextmv-1.0.0.dev2.dist-info → nextmv-1.0.0.dev4.dist-info}/licenses/LICENSE +0 -0
@@ -17,15 +17,6 @@ app = typer.Typer()
17
17
 
18
18
  @app.command()
19
19
  def create(
20
- name: Annotated[
21
- str,
22
- typer.Option(
23
- "--name",
24
- "-n",
25
- help="A name for the application.",
26
- metavar="NAME",
27
- ),
28
- ],
29
20
  app_id: Annotated[
30
21
  str | None,
31
22
  typer.Option(
@@ -79,47 +70,55 @@ def create(
79
70
  help="Whether the application is a workflow.",
80
71
  ),
81
72
  ] = False,
73
+ name: Annotated[
74
+ str | None,
75
+ typer.Option(
76
+ "--name",
77
+ "-n",
78
+ help="An optional name for the application. If not provided, the application ID will be used as the name.",
79
+ metavar="NAME",
80
+ ),
81
+ ] = None,
82
82
  profile: ProfileOption = None,
83
83
  ) -> None:
84
84
  """
85
85
  Create a new Nextmv Cloud application.
86
86
 
87
- Use the [code]--exist-ok[/code] flag to avoid errors when creating an
88
- application with an ID that already exists. This is useful for scripts that
89
- need to ensure an application exists without worrying about whether it was
90
- created previously.
87
+ Use the --exist-ok flag to avoid errors when creating an application with
88
+ an ID that already exists. This is useful for scripts that need to ensure
89
+ an application exists without worrying about whether it was created
90
+ previously.
91
91
 
92
- An application can be marked as a workflow using the
93
- [code]--is-workflow[/code] flag. Workflows allow for more complex
94
- decision-making processes by leveraging
92
+ An application can be marked as a workflow using the --is-workflow flag.
93
+ Workflows allow for more complex decision-making processes by leveraging
95
94
  [link=https://github.com/nextmv-io/nextpipe][bold]Nextpipe[/bold][/link] to
96
95
  orchestrate multiple decision models.
97
96
 
98
97
  [bold][underline]Examples[/underline][/bold]
99
98
 
100
99
  - Create an application with the name [magenta]Hare App[/magenta]. A random ID will be generated.
101
- $ [green]nextmv cloud app create --name "Hare App"[/green]
100
+ $ [dim]nextmv cloud app create --name "Hare App"[/dim]
102
101
 
103
102
  - Create an application with the specific ID [magenta]hare-app[/magenta].
104
- $ [green]nextmv cloud app create --name "Hare App" --app-id hare-app[/green]
103
+ $ [dim]nextmv cloud app create --name "Hare App" --app-id hare-app[/dim]
105
104
 
106
105
  - Create an application with an ID and description.
107
- $ [green]nextmv cloud app create --name "Hare App" --app-id hare-app \\
108
- --description "An application for routing hares"[/green]
106
+ $ [dim]nextmv cloud app create --name "Hare App" --app-id hare-app \\
107
+ --description "An application for routing hares"[/dim]
109
108
 
110
109
  - Create an application, or get it if it already exists.
111
- $ [green]nextmv cloud app create --name "Hare App" --app-id hare-app --exist-ok[/green]
110
+ $ [dim]nextmv cloud app create --name "Hare App" --app-id hare-app --exist-ok[/dim]
112
111
 
113
112
  - Create a workflow application.
114
- $ [green]nextmv cloud app create --name "Hare Workflow" --app-id hare-workflow --is-workflow[/green]
113
+ $ [dim]nextmv cloud app create --name "Hare Workflow" --app-id hare-workflow --is-workflow[/dim]
115
114
 
116
115
  - Create an application with a default instance ID.
117
- $ [green]nextmv cloud app create --name "Hare App" --app-id hare-app \\
118
- --default-instance-id burrow[/green]
116
+ $ [dim]nextmv cloud app create --name "Hare App" --app-id hare-app \\
117
+ --default-instance-id burrow[/dim]
119
118
 
120
119
  - Create an application with a default experiment instance.
121
- $ [green]nextmv cloud app create --name "Hare App" --app-id hare-app \\
122
- --default-experiment-instance experiment-v1[/green]
120
+ $ [dim]nextmv cloud app create --name "Hare App" --app-id hare-app \\
121
+ --default-experiment-instance experiment-v1[/dim]
123
122
  """
124
123
 
125
124
  client = build_client(profile)
@@ -5,9 +5,9 @@ This module defines the cloud app delete command for the Nextmv CLI.
5
5
  from typing import Annotated
6
6
 
7
7
  import typer
8
- from rich.prompt import Confirm
9
8
 
10
9
  from nextmv.cli.configuration.config import build_app
10
+ from nextmv.cli.confirm import get_confirmation
11
11
  from nextmv.cli.message import info, success
12
12
  from nextmv.cli.options import AppIDOption, ProfileOption
13
13
 
@@ -31,22 +31,21 @@ def delete(
31
31
  """
32
32
  Deletes a Nextmv Cloud application.
33
33
 
34
- This action is permanent and cannot be undone. Use the [code]--yes[/code]
34
+ This action is permanent and cannot be undone. Use the --yes
35
35
  flag to skip the confirmation prompt.
36
36
 
37
37
  [bold][underline]Examples[/underline][/bold]
38
38
 
39
39
  - Delete the application with the ID [magenta]hare-app[/magenta].
40
- $ [green]nextmv cloud app delete --app-id hare-app[/green]
40
+ $ [dim]nextmv cloud app delete --app-id hare-app[/dim]
41
41
 
42
42
  - Delete the application with the ID [magenta]hare-app[/magenta] without confirmation prompt.
43
- $ [green]nextmv cloud app delete --app-id hare-app --yes[/green]
43
+ $ [dim]nextmv cloud app delete --app-id hare-app --yes[/dim]
44
44
  """
45
45
 
46
46
  if not yes:
47
- confirm = Confirm.ask(
47
+ confirm = get_confirmation(
48
48
  f"Are you sure you want to delete application [magenta]{app_id}[/magenta]? This action cannot be undone.",
49
- default=False,
50
49
  )
51
50
 
52
51
  if not confirm:
@@ -27,11 +27,11 @@ def exists(
27
27
  [bold][underline]Examples[/underline][/bold]
28
28
 
29
29
  - Check if the application with the ID [magenta]hare-app[/magenta] exists.
30
- $ [green]nextmv cloud app exists --app-id hare-app[/green]
30
+ $ [dim]nextmv cloud app exists --app-id hare-app[/dim]
31
31
 
32
32
  - Check if the application with the ID [magenta]hare-app[/magenta] exists.
33
33
  Use the profile named [magenta]hare[/magenta].
34
- $ [green]nextmv cloud app exists --app-id hare-app --profile hare[/green]
34
+ $ [dim]nextmv cloud app exists --app-id hare-app --profile hare[/dim]
35
35
  """
36
36
 
37
37
  client = build_client(profile)
@@ -39,11 +39,11 @@ def get(
39
39
  [bold][underline]Examples[/underline][/bold]
40
40
 
41
41
  - Get the application with the ID [magenta]hare-app[/magenta].
42
- $ [green]nextmv cloud app get --app-id hare-app[/green]
42
+ $ [dim]nextmv cloud app get --app-id hare-app[/dim]
43
43
 
44
44
  - Get the application with the ID [magenta]hare-app[/magenta] and save the information to an
45
45
  [magenta]app.json[/magenta] file.
46
- $ [green]nextmv cloud app get --app-id hare-app --output app.json[/green]
46
+ $ [dim]nextmv cloud app get --app-id hare-app --output app.json[/dim]
47
47
  """
48
48
 
49
49
  client = build_client(profile)
@@ -35,13 +35,13 @@ def list(
35
35
  [bold][underline]Examples[/underline][/bold]
36
36
 
37
37
  - List all applications.
38
- $ [green]nextmv cloud app list[/green]
38
+ $ [dim]nextmv cloud app list[/dim]
39
39
 
40
40
  - List all applications using the profile named [magenta]hare[/magenta].
41
- $ [green]nextmv cloud app list --profile hare[/green]
41
+ $ [dim]nextmv cloud app list --profile hare[/dim]
42
42
 
43
43
  - List all applications and save the information to an [magenta]apps.json[/magenta] file.
44
- $ [green]nextmv cloud app list --output apps.json[/green]
44
+ $ [dim]nextmv cloud app list --output apps.json[/dim]
45
45
  """
46
46
 
47
47
  client = build_client(profile)
@@ -2,12 +2,17 @@
2
2
  This module defines the cloud app push command for the Nextmv CLI.
3
3
  """
4
4
 
5
+ from datetime import datetime, timezone
5
6
  from typing import Annotated
6
7
 
7
8
  import typer
8
9
 
9
10
  from nextmv.cli.configuration.config import build_app
11
+ from nextmv.cli.confirm import get_confirmation
12
+ from nextmv.cli.message import error, in_progress, info, success
10
13
  from nextmv.cli.options import AppIDOption, ProfileOption
14
+ from nextmv.cloud.application import Application
15
+ from nextmv.cloud.instance import Instance
11
16
  from nextmv.manifest import Manifest
12
17
 
13
18
  # Set up subcommand application.
@@ -35,44 +40,109 @@ def push(
35
40
  metavar="MANIFEST_PATH",
36
41
  ),
37
42
  ] = None,
38
- no_version: Annotated[
43
+ # Options for unsupervised prompts.
44
+ auto_create_yes: Annotated[
39
45
  bool,
40
46
  typer.Option(
41
- "--no-version",
42
- "-n",
43
- help="The application will be pushed without creating a new version. "
44
- "Default is to create a new version on each push.",
45
- rich_help_panel="Versioning control",
47
+ "--auto-create-yes",
48
+ "-cy",
49
+ help="Create a new version and instance after push. "
50
+ "Skips confirmation prompt with [magenta]yes[/magenta]. Useful for non-interactive sessions.",
51
+ rich_help_panel="Automatic creation and updating",
46
52
  ),
47
53
  ] = False,
54
+ auto_create_no: Annotated[
55
+ bool,
56
+ typer.Option(
57
+ "--auto-create-no",
58
+ "-cn",
59
+ help="Do not create a new version and instance after push. "
60
+ "Skips confirmation prompt with [magenta]no[/magenta]. Useful for non-interactive sessions.",
61
+ rich_help_panel="Automatic creation and updating",
62
+ ),
63
+ ] = False,
64
+ update_default_instance_yes: Annotated[
65
+ bool,
66
+ typer.Option(
67
+ "--update-default-instance-yes",
68
+ "-uy",
69
+ help="Update the default instance of the app after version/instance creation. "
70
+ "Skips confirmation prompt with [magenta]yes[/magenta]. "
71
+ "Useful for non-interactive sessions. Activates --auto-create-yes.",
72
+ rich_help_panel="Automatic creation and updating",
73
+ ),
74
+ ] = False,
75
+ update_default_instance_no: Annotated[
76
+ bool,
77
+ typer.Option(
78
+ "--update-default-instance-no",
79
+ "-un",
80
+ help="Do not update the default instance of the app after version/instance creation. "
81
+ "Skips confirmation prompt with [magenta]no[/magenta]. "
82
+ "Useful for non-interactive sessions. Activates --auto-create-yes.",
83
+ rich_help_panel="Automatic creation and updating",
84
+ ),
85
+ ] = False,
86
+ # Options for version creation.
48
87
  version_id: Annotated[
49
88
  str | None,
50
89
  typer.Option(
51
90
  "--version-id",
52
- "-v",
53
- help="Custom version ID when pushing the application. Automatically generated if not provided.",
54
- rich_help_panel="Versioning control",
91
+ help="Custom version ID when pushing the application. Automatically generated if not provided. "
92
+ "Activates --auto-create-yes.",
93
+ rich_help_panel="Version control",
55
94
  metavar="VERSION_ID",
56
95
  ),
57
96
  ] = None,
97
+ version_description: Annotated[
98
+ str | None,
99
+ typer.Option(
100
+ "--version-description",
101
+ help="Custom version description when pushing the application. Automatically generated if not provided. "
102
+ "Activates --auto-create-yes.",
103
+ rich_help_panel="Version control",
104
+ metavar="VERSION_DESCRIPTION",
105
+ ),
106
+ ] = None,
58
107
  version_name: Annotated[
59
108
  str | None,
60
109
  typer.Option(
61
110
  "--version-name",
62
- "-e",
63
- help="Custom version name when pushing the application. Automatically generated if not provided.",
64
- rich_help_panel="Versioning control",
111
+ help="Custom version name when pushing the application. Automatically generated if not provided. "
112
+ "Activates --auto-create-yes.",
113
+ rich_help_panel="Version control",
65
114
  metavar="VERSION_NAME",
66
115
  ),
67
116
  ] = None,
68
- version_description: Annotated[
117
+ # Options for instance creation.
118
+ instance_id: Annotated[
69
119
  str | None,
70
120
  typer.Option(
71
- "--version-description",
72
- "-r",
73
- help="Custom version description when pushing the application. Automatically generated if not provided.",
74
- rich_help_panel="Versioning control",
75
- metavar="VERSION_DESCRIPTION",
121
+ "--instance-id",
122
+ help="Custom instance ID when pushing the application. Automatically generated if not provided. "
123
+ "Activates --auto-create-yes.",
124
+ rich_help_panel="Instance control",
125
+ metavar="INSTANCE_ID",
126
+ ),
127
+ ] = None,
128
+ instance_description: Annotated[
129
+ str | None,
130
+ typer.Option(
131
+ "--instance-description",
132
+ help="Custom instance description when pushing the application. Automatically generated if not provided. "
133
+ "Activates --auto-create-yes.",
134
+ rich_help_panel="Instance control",
135
+ metavar="INSTANCE_DESCRIPTION",
136
+ ),
137
+ ] = None,
138
+ instance_name: Annotated[
139
+ str | None,
140
+ typer.Option(
141
+ "--instance-name",
142
+ help="Custom instance name when pushing the application. Automatically generated if not provided. "
143
+ "Activates --auto-create-yes.",
144
+ rich_help_panel="Instance control",
145
+ metavar="INSTANCE_NAME",
76
146
  ),
77
147
  ] = None,
78
148
  profile: ProfileOption = None,
@@ -80,49 +150,75 @@ def push(
80
150
  """
81
151
  Push (deploy) a Nextmv application to Nextmv Cloud.
82
152
 
83
- Use the [code]--app-dir[/code] option to specify the path to your
84
- application's root directory. By default, the current working directory is
85
- used.
153
+ Use the --app-dir option to specify the path to your application's root
154
+ directory. By default, the current working directory is used.
155
+
156
+ You can also provide a custom manifest file using the --manifest option. If
157
+ not provided, the CLI will look for a file named
158
+ [magenta]app.yaml[/magenta] in the application's root.
159
+
86
160
 
87
- You can also provide a custom manifest file using the [code]--manifest[/code]
88
- option. If not provided, the CLI will look for a file named [magenta]app.yaml[/magenta]
89
- in the application's root.
161
+ By default, this command only pushes the app. After the push, you will be
162
+ prompted to create a new version and instance. Use --auto-create-yes to
163
+ automatically create a new version and instance (skipping the prompt), or
164
+ --auto-create-no to skip creation.
90
165
 
91
- The default behavior of this command is to create a new application version
92
- [italic]after[/italic] the app has been pushed. You can use the
93
- [code]--no-version[/code] option to skip this step. The
94
- [code]--version-...[/code] options allow you to customize the attributes of
95
- the version that is created. If any of these options are not provided,
96
- automatically generated values will be used.
166
+ The --version-id, --version-name, and --version-description options allow
167
+ you to customize the new version. The --instance-id, --instance-name, and
168
+ --instance-description options allow you to customize the new instance. If
169
+ any of these options are provided, --auto-create-yes is automatically
170
+ activated.
171
+
172
+ After version/instance creation, you will be prompted to set the new
173
+ instance as the default. Use --update-default-instance-yes to automatically
174
+ update the default instance (skipping the prompt), or
175
+ --update-default-instance-no to skip updating. Providing either of these
176
+ options automatically activates --auto-create-yes.
97
177
 
98
178
  [bold][underline]Examples[/underline][/bold]
99
179
 
100
180
  - Push an application, with ID [magenta]hare-app[/magenta], from the current directory.
101
- $ [green]nextmv cloud app push --app-id hare-app[/green]
181
+ $ [dim]nextmv cloud app push --app-id hare-app[/dim]
102
182
 
103
183
  - Push an application, with ID [magenta]hare-app[/magenta], from the [magenta]./my-app[/magenta] directory.
104
- $ [green]nextmv cloud app push --app-id hare-app --app-dir ./my-app[/green]
184
+ $ [dim]nextmv cloud app push --app-id hare-app --app-dir ./my-app[/dim]
105
185
 
106
186
  - Push an application, with ID [magenta]hare-app[/magenta], using a custom manifest file.
107
- $ [green]nextmv cloud app push --app-id hare-app --manifest ./custom-manifest.yaml[/green]
187
+ $ [dim]nextmv cloud app push --app-id hare-app --manifest ./custom-manifest.yaml[/dim]
188
+
189
+ - Push an application, with ID [magenta]hare-app[/magenta], from a specific [magenta]./my-app[/magenta] directory
190
+ with a custom manifest.
191
+ $ [dim]nextmv cloud app push --app-id hare-app --app-dir ./my-app --manifest ./custom-manifest.yaml[/dim]
108
192
 
109
- - Push an application, with ID [magenta]hare-app[/magenta], from a specific
110
- [magenta]./my-app[/magenta] directory with a custom manifest under [magenta]./custom-manifest.yaml[/magenta].
111
- $ [green]nextmv cloud app push --app-id hare-app --app-dir ./my-app \\
112
- --manifest ./custom-manifest.yaml[/green]
193
+ - Push and automatically create a new version and instance (no prompt).
194
+ $ [dim]nextmv cloud app push --app-id hare-app --auto-create-yes[/dim]
113
195
 
114
- - Push an application without creating a new version.
115
- $ [green]nextmv cloud app push --app-id hare-app --no-version[/green]
196
+ - Push and skip version/instance creation (no prompt).
197
+ $ [dim]nextmv cloud app push --app-id hare-app --auto-create-no[/dim]
116
198
 
117
- - Push an application with a custom version ID.
118
- $ [green]nextmv cloud app push --app-id hare-app --version-id v1.0.0[/green]
199
+ - Push and automatically create a new version and instance, then set the new instance as default (no prompts).
200
+ $ [dim]nextmv cloud app push --app-id hare-app --auto-create-yes --update-default-instance-yes[/dim]
119
201
 
120
- - Push an application with custom version ID, name, and description.
121
- $ [green]nextmv cloud app push --app-id hare-app --version-id v1.0.0 \\
202
+ - Push and create a new version with a custom version ID (auto-create is activated).
203
+ $ [dim]nextmv cloud app push --app-id hare-app --version-id v1.0.0[/dim]
204
+
205
+ - Push with custom version and instance attributes, and set the new instance as default (all prompts skipped).
206
+ $ [dim]nextmv cloud app push --app-id hare-app \\
207
+ --version-id v1.0.0 \\
122
208
  --version-name "Release 1.0.0" \\
123
- --version-description "First stable release"[/green]
209
+ --version-description "First stable release" \\
210
+ --instance-id inst-1 \\
211
+ --instance-name "Production Instance" \\
212
+ --instance-description "Main deployment" \\
213
+ --update-default-instance-yes[/dim]
124
214
  """
125
215
 
216
+ if auto_create_yes and auto_create_no:
217
+ error("Cannot specify both --auto-create-yes and --auto-create-no.")
218
+
219
+ if update_default_instance_yes and update_default_instance_no:
220
+ error("Cannot specify both --update-default-instance-yes and --update-default-instance-no.")
221
+
126
222
  cloud_app = build_app(app_id=app_id, profile=profile)
127
223
  loaded_manifest = Manifest.from_yaml(dirpath=manifest) if manifest is not None and manifest != "" else None
128
224
  cloud_app.push(
@@ -130,8 +226,136 @@ def push(
130
226
  app_dir=app_dir,
131
227
  verbose=True,
132
228
  rich_print=True,
133
- no_version=no_version,
229
+ )
230
+
231
+ instance, should_update = _handle_version_instance_creation(
232
+ cloud_app=cloud_app,
233
+ app_id=app_id,
234
+ auto_create_yes=auto_create_yes,
235
+ auto_create_no=auto_create_no,
236
+ update_default_instance_yes=update_default_instance_yes,
237
+ update_default_instance_no=update_default_instance_no,
134
238
  version_id=version_id,
135
239
  version_name=version_name,
136
240
  version_description=version_description,
241
+ instance_id=instance_id,
242
+ instance_name=instance_name,
243
+ instance_description=instance_description,
244
+ )
245
+ _handle_instance_update(
246
+ cloud_app=cloud_app,
247
+ app_id=app_id,
248
+ instance=instance,
249
+ should_update=should_update,
250
+ update_default_instance_yes=update_default_instance_yes,
251
+ update_default_instance_no=update_default_instance_no,
252
+ )
253
+
254
+
255
+ def _handle_version_instance_creation(
256
+ cloud_app: Application,
257
+ app_id: str,
258
+ auto_create_yes: bool,
259
+ auto_create_no: bool,
260
+ update_default_instance_yes: bool,
261
+ update_default_instance_no: bool,
262
+ version_id: str | None,
263
+ version_name: str | None,
264
+ version_description: str | None,
265
+ instance_id: str | None,
266
+ instance_name: str | None,
267
+ instance_description: str | None,
268
+ ) -> tuple[Instance, bool]:
269
+ if auto_create_no:
270
+ info(
271
+ msg="--auto-create-no activated, will not create a new version and instance.",
272
+ emoji=":bulb:",
273
+ )
274
+ return None, False
275
+
276
+ # Determine if we need to create a new version and instance based on the options provided.
277
+ version_provided = version_id is not None or version_name is not None or version_description is not None
278
+ instance_provided = instance_id is not None or instance_name is not None or instance_description is not None
279
+ if version_provided or instance_provided or update_default_instance_yes or update_default_instance_no:
280
+ auto_create_yes = True
281
+
282
+ if not auto_create_yes:
283
+ should_create = get_confirmation(
284
+ f"Do you want to create a new version and instance for application [magenta]{app_id}[/magenta] now?"
285
+ )
286
+
287
+ if not should_create:
288
+ info(
289
+ msg="Will not create a new version and instance.",
290
+ emoji=":bulb:",
291
+ )
292
+ return None, False
293
+
294
+ in_progress("Creating a new version and instance...")
295
+ now = datetime.now(timezone.utc)
296
+
297
+ if version_description is None or version_description == "":
298
+ version_description = f"Version created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
299
+
300
+ version = cloud_app.new_version(
301
+ id=version_id,
302
+ name=version_name,
303
+ description=version_description,
304
+ )
305
+ success(f"New version [magenta]{version.id}[/magenta] created for application [magenta]{app_id}[/magenta].")
306
+
307
+ if instance_description is None or instance_description == "":
308
+ instance_description = f"Instance created automatically from push at {now.strftime('%Y-%m-%dT%H:%M:%SZ')}"
309
+
310
+ instance = cloud_app.new_instance(
311
+ version_id=version.id,
312
+ id=instance_id,
313
+ name=instance_name,
314
+ description=instance_description,
315
+ )
316
+ success(
317
+ f"New instance [magenta]{instance.id}[/magenta] created using version [magenta]{version.id}[/magenta] "
318
+ f"for application [magenta]{app_id}[/magenta]."
319
+ )
320
+
321
+ return instance, True
322
+
323
+
324
+ def _handle_instance_update(
325
+ cloud_app: Application,
326
+ app_id: str,
327
+ instance: Instance,
328
+ should_update: bool,
329
+ update_default_instance_yes: bool,
330
+ update_default_instance_no: bool,
331
+ ) -> None:
332
+ if instance is None or not should_update:
333
+ return
334
+
335
+ if update_default_instance_no:
336
+ info(
337
+ msg="--update-default-instance-no activated, will not update the default instance.",
338
+ emoji=":bulb:",
339
+ )
340
+ return
341
+
342
+ if not update_default_instance_yes:
343
+ should_update = get_confirmation(
344
+ f"Do you want to set instance [magenta]{instance.id}[/magenta] as the default instance for "
345
+ f"application [magenta]{app_id}[/magenta]?"
346
+ )
347
+
348
+ if not should_update:
349
+ info(
350
+ msg=f"Default instance for application [magenta]{app_id}[/magenta] not updated.",
351
+ emoji=":bulb:",
352
+ )
353
+ return
354
+
355
+ in_progress(
356
+ f"Updating default instance for application [magenta]{app_id}[/magenta] to [magenta]{instance.id}[/magenta]..."
357
+ )
358
+ cloud_app.update(default_instance_id=instance.id)
359
+ success(
360
+ f"Default instance for application [magenta]{app_id}[/magenta] updated to [magenta]{instance.id}[/magenta].",
137
361
  )
@@ -74,33 +74,33 @@ def update(
74
74
  [bold][underline]Examples[/underline][/bold]
75
75
 
76
76
  - Update an application's name.
77
- $ [green]nextmv cloud app update --app-id hare-app --name "New Hare App"[/green]
77
+ $ [dim]nextmv cloud app update --app-id hare-app --name "New Hare App"[/dim]
78
78
 
79
79
  - Update an application's description.
80
- $ [green]nextmv cloud app update --app-id hare-app --name "Hare App" \\
81
- --description "An updated description for routing hares"[/green]
80
+ $ [dim]nextmv cloud app update --app-id hare-app --name "Hare App" \\
81
+ --description "An updated description for routing hares"[/dim]
82
82
 
83
83
  - Update an application's default instance ID.
84
- $ [green]nextmv cloud app update --app-id hare-app --name "Hare App" \\
85
- --default-instance-id burrow[/green]
84
+ $ [dim]nextmv cloud app update --app-id hare-app --name "Hare App" \\
85
+ --default-instance-id burrow[/dim]
86
86
 
87
87
  - Update an application's default experiment instance.
88
- $ [green]nextmv cloud app update --app-id hare-app --name "Hare App" \\
89
- --default-experiment-instance experiment-v1[/green]
88
+ $ [dim]nextmv cloud app update --app-id hare-app --name "Hare App" \\
89
+ --default-experiment-instance experiment-v1[/dim]
90
90
 
91
91
  - Update multiple application properties at once.
92
- $ [green]nextmv cloud app update --app-id hare-app --name "Hare App" \\
92
+ $ [dim]nextmv cloud app update --app-id hare-app --name "Hare App" \\
93
93
  --description "Updated description" --default-instance-id burrow \\
94
- --default-experiment-instance experiment-v1[/green]
94
+ --default-experiment-instance experiment-v1[/dim]
95
95
 
96
96
  - Update an application and save the updated information to an [magenta]updated_app.json[/magenta] file.
97
- $ [green]nextmv cloud app update --app-id hare-app --name "New Hare App" --output updated_app.json[/green]
97
+ $ [dim]nextmv cloud app update --app-id hare-app --name "New Hare App" --output updated_app.json[/dim]
98
98
  """
99
99
 
100
100
  if name is None and description is None and default_instance_id is None and default_experiment_instance is None:
101
101
  error(
102
- "Provide at least one option to update: [code]--name[/code], [code]--description[/code], "
103
- "[code]--default-instance-id[/code], or [code]--default-experiment-instance[/code]."
102
+ "Provide at least one option to update: --name, --description, "
103
+ "--default-instance-id, or --default-experiment-instance."
104
104
  )
105
105
 
106
106
  cloud_app = build_app(app_id=app_id, profile=profile)