nextmv 0.40.0__py3-none-any.whl → 1.0.0__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 (163) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +1 -2
  3. nextmv/__init__.py +2 -4
  4. nextmv/cli/CONTRIBUTING.md +583 -0
  5. nextmv/cli/cloud/__init__.py +49 -0
  6. nextmv/cli/cloud/acceptance/__init__.py +27 -0
  7. nextmv/cli/cloud/acceptance/create.py +391 -0
  8. nextmv/cli/cloud/acceptance/delete.py +64 -0
  9. nextmv/cli/cloud/acceptance/get.py +103 -0
  10. nextmv/cli/cloud/acceptance/list.py +62 -0
  11. nextmv/cli/cloud/acceptance/update.py +95 -0
  12. nextmv/cli/cloud/account/__init__.py +28 -0
  13. nextmv/cli/cloud/account/create.py +83 -0
  14. nextmv/cli/cloud/account/delete.py +59 -0
  15. nextmv/cli/cloud/account/get.py +66 -0
  16. nextmv/cli/cloud/account/update.py +70 -0
  17. nextmv/cli/cloud/app/__init__.py +35 -0
  18. nextmv/cli/cloud/app/create.py +140 -0
  19. nextmv/cli/cloud/app/delete.py +57 -0
  20. nextmv/cli/cloud/app/exists.py +44 -0
  21. nextmv/cli/cloud/app/get.py +66 -0
  22. nextmv/cli/cloud/app/list.py +61 -0
  23. nextmv/cli/cloud/app/push.py +432 -0
  24. nextmv/cli/cloud/app/update.py +124 -0
  25. nextmv/cli/cloud/batch/__init__.py +29 -0
  26. nextmv/cli/cloud/batch/create.py +452 -0
  27. nextmv/cli/cloud/batch/delete.py +64 -0
  28. nextmv/cli/cloud/batch/get.py +104 -0
  29. nextmv/cli/cloud/batch/list.py +63 -0
  30. nextmv/cli/cloud/batch/metadata.py +66 -0
  31. nextmv/cli/cloud/batch/update.py +95 -0
  32. nextmv/cli/cloud/data/__init__.py +26 -0
  33. nextmv/cli/cloud/data/upload.py +162 -0
  34. nextmv/cli/cloud/ensemble/__init__.py +33 -0
  35. nextmv/cli/cloud/ensemble/create.py +413 -0
  36. nextmv/cli/cloud/ensemble/delete.py +63 -0
  37. nextmv/cli/cloud/ensemble/get.py +65 -0
  38. nextmv/cli/cloud/ensemble/list.py +63 -0
  39. nextmv/cli/cloud/ensemble/update.py +103 -0
  40. nextmv/cli/cloud/input_set/__init__.py +32 -0
  41. nextmv/cli/cloud/input_set/create.py +168 -0
  42. nextmv/cli/cloud/input_set/delete.py +64 -0
  43. nextmv/cli/cloud/input_set/get.py +63 -0
  44. nextmv/cli/cloud/input_set/list.py +63 -0
  45. nextmv/cli/cloud/input_set/update.py +123 -0
  46. nextmv/cli/cloud/instance/__init__.py +35 -0
  47. nextmv/cli/cloud/instance/create.py +289 -0
  48. nextmv/cli/cloud/instance/delete.py +61 -0
  49. nextmv/cli/cloud/instance/exists.py +39 -0
  50. nextmv/cli/cloud/instance/get.py +62 -0
  51. nextmv/cli/cloud/instance/list.py +60 -0
  52. nextmv/cli/cloud/instance/update.py +216 -0
  53. nextmv/cli/cloud/managed_input/__init__.py +31 -0
  54. nextmv/cli/cloud/managed_input/create.py +144 -0
  55. nextmv/cli/cloud/managed_input/delete.py +64 -0
  56. nextmv/cli/cloud/managed_input/get.py +63 -0
  57. nextmv/cli/cloud/managed_input/list.py +60 -0
  58. nextmv/cli/cloud/managed_input/update.py +97 -0
  59. nextmv/cli/cloud/run/__init__.py +37 -0
  60. nextmv/cli/cloud/run/cancel.py +37 -0
  61. nextmv/cli/cloud/run/create.py +524 -0
  62. nextmv/cli/cloud/run/get.py +199 -0
  63. nextmv/cli/cloud/run/input.py +86 -0
  64. nextmv/cli/cloud/run/list.py +80 -0
  65. nextmv/cli/cloud/run/logs.py +166 -0
  66. nextmv/cli/cloud/run/metadata.py +67 -0
  67. nextmv/cli/cloud/run/track.py +500 -0
  68. nextmv/cli/cloud/scenario/__init__.py +29 -0
  69. nextmv/cli/cloud/scenario/create.py +451 -0
  70. nextmv/cli/cloud/scenario/delete.py +61 -0
  71. nextmv/cli/cloud/scenario/get.py +102 -0
  72. nextmv/cli/cloud/scenario/list.py +63 -0
  73. nextmv/cli/cloud/scenario/metadata.py +67 -0
  74. nextmv/cli/cloud/scenario/update.py +93 -0
  75. nextmv/cli/cloud/secrets/__init__.py +33 -0
  76. nextmv/cli/cloud/secrets/create.py +206 -0
  77. nextmv/cli/cloud/secrets/delete.py +63 -0
  78. nextmv/cli/cloud/secrets/get.py +66 -0
  79. nextmv/cli/cloud/secrets/list.py +60 -0
  80. nextmv/cli/cloud/secrets/update.py +144 -0
  81. nextmv/cli/cloud/shadow/__init__.py +33 -0
  82. nextmv/cli/cloud/shadow/create.py +184 -0
  83. nextmv/cli/cloud/shadow/delete.py +64 -0
  84. nextmv/cli/cloud/shadow/get.py +61 -0
  85. nextmv/cli/cloud/shadow/list.py +63 -0
  86. nextmv/cli/cloud/shadow/metadata.py +66 -0
  87. nextmv/cli/cloud/shadow/start.py +43 -0
  88. nextmv/cli/cloud/shadow/stop.py +53 -0
  89. nextmv/cli/cloud/shadow/update.py +96 -0
  90. nextmv/cli/cloud/switchback/__init__.py +33 -0
  91. nextmv/cli/cloud/switchback/create.py +151 -0
  92. nextmv/cli/cloud/switchback/delete.py +64 -0
  93. nextmv/cli/cloud/switchback/get.py +62 -0
  94. nextmv/cli/cloud/switchback/list.py +63 -0
  95. nextmv/cli/cloud/switchback/metadata.py +68 -0
  96. nextmv/cli/cloud/switchback/start.py +43 -0
  97. nextmv/cli/cloud/switchback/stop.py +53 -0
  98. nextmv/cli/cloud/switchback/update.py +96 -0
  99. nextmv/cli/cloud/upload/__init__.py +22 -0
  100. nextmv/cli/cloud/upload/create.py +39 -0
  101. nextmv/cli/cloud/version/__init__.py +33 -0
  102. nextmv/cli/cloud/version/create.py +96 -0
  103. nextmv/cli/cloud/version/delete.py +61 -0
  104. nextmv/cli/cloud/version/exists.py +39 -0
  105. nextmv/cli/cloud/version/get.py +62 -0
  106. nextmv/cli/cloud/version/list.py +60 -0
  107. nextmv/cli/cloud/version/update.py +92 -0
  108. nextmv/cli/community/__init__.py +24 -0
  109. nextmv/cli/community/clone.py +20 -204
  110. nextmv/cli/community/list.py +61 -126
  111. nextmv/cli/configuration/__init__.py +23 -0
  112. nextmv/cli/configuration/config.py +103 -6
  113. nextmv/cli/configuration/create.py +17 -18
  114. nextmv/cli/configuration/delete.py +25 -13
  115. nextmv/cli/configuration/list.py +4 -4
  116. nextmv/cli/confirm.py +34 -0
  117. nextmv/cli/main.py +68 -36
  118. nextmv/cli/message.py +170 -0
  119. nextmv/cli/options.py +196 -0
  120. nextmv/cli/version.py +20 -1
  121. nextmv/cloud/__init__.py +17 -38
  122. nextmv/cloud/acceptance_test.py +20 -83
  123. nextmv/cloud/account.py +269 -30
  124. nextmv/cloud/application/__init__.py +898 -0
  125. nextmv/cloud/application/_acceptance.py +424 -0
  126. nextmv/cloud/application/_batch_scenario.py +845 -0
  127. nextmv/cloud/application/_ensemble.py +251 -0
  128. nextmv/cloud/application/_input_set.py +263 -0
  129. nextmv/cloud/application/_instance.py +289 -0
  130. nextmv/cloud/application/_managed_input.py +227 -0
  131. nextmv/cloud/application/_run.py +1393 -0
  132. nextmv/cloud/application/_secrets.py +294 -0
  133. nextmv/cloud/application/_shadow.py +320 -0
  134. nextmv/cloud/application/_switchback.py +332 -0
  135. nextmv/cloud/application/_utils.py +54 -0
  136. nextmv/cloud/application/_version.py +304 -0
  137. nextmv/cloud/batch_experiment.py +6 -2
  138. nextmv/cloud/community.py +446 -0
  139. nextmv/cloud/instance.py +11 -1
  140. nextmv/cloud/integration.py +8 -5
  141. nextmv/cloud/package.py +50 -9
  142. nextmv/cloud/shadow.py +254 -0
  143. nextmv/cloud/switchback.py +228 -0
  144. nextmv/deprecated.py +5 -3
  145. nextmv/input.py +20 -88
  146. nextmv/local/application.py +3 -15
  147. nextmv/local/runner.py +1 -1
  148. nextmv/model.py +50 -11
  149. nextmv/options.py +11 -256
  150. nextmv/output.py +0 -62
  151. nextmv/polling.py +54 -16
  152. nextmv/run.py +84 -37
  153. nextmv/status.py +1 -51
  154. {nextmv-0.40.0.dist-info → nextmv-1.0.0.dist-info}/METADATA +37 -11
  155. nextmv-1.0.0.dist-info/RECORD +185 -0
  156. nextmv-1.0.0.dist-info/entry_points.txt +2 -0
  157. nextmv/cli/community/community.py +0 -24
  158. nextmv/cli/configuration/configuration.py +0 -23
  159. nextmv/cli/error.py +0 -22
  160. nextmv/cloud/application.py +0 -4204
  161. nextmv-0.40.0.dist-info/RECORD +0 -66
  162. {nextmv-0.40.0.dist-info → nextmv-1.0.0.dist-info}/WHEEL +0 -0
  163. {nextmv-0.40.0.dist-info → nextmv-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,63 @@
1
+ """
2
+ This module defines the cloud batch list command for the Nextmv CLI.
3
+ """
4
+
5
+ import json
6
+ from typing import Annotated
7
+
8
+ import typer
9
+
10
+ from nextmv.cli.configuration.config import build_app
11
+ from nextmv.cli.message import in_progress, print_json, success
12
+ from nextmv.cli.options import AppIDOption, ProfileOption
13
+
14
+ # Set up subcommand application.
15
+ app = typer.Typer()
16
+
17
+
18
+ @app.command()
19
+ def list(
20
+ app_id: AppIDOption,
21
+ output: Annotated[
22
+ str | None,
23
+ typer.Option(
24
+ "--output",
25
+ "-o",
26
+ help="Saves the list of batch experiments to this location.",
27
+ metavar="OUTPUT_PATH",
28
+ ),
29
+ ] = None,
30
+ profile: ProfileOption = None,
31
+ ) -> None:
32
+ """
33
+ List all Nextmv Cloud batch experiments for an application.
34
+
35
+ This command retrieves all batch experiments associated with the specified
36
+ application.
37
+
38
+ [bold][underline]Examples[/underline][/bold]
39
+
40
+ - List all batch experiments for application [magenta]hare-app[/magenta].
41
+ $ [dim]nextmv cloud batch list --app-id hare-app[/dim]
42
+
43
+ - List all batch experiments and save to a file.
44
+ $ [dim]nextmv cloud batch list --app-id hare-app --output experiments.json[/dim]
45
+
46
+ - List all batch experiments using a specific profile.
47
+ $ [dim]nextmv cloud batch list --app-id hare-app --profile prod[/dim]
48
+ """
49
+
50
+ cloud_app = build_app(app_id=app_id, profile=profile)
51
+ in_progress(msg="Listing batch experiments...")
52
+ batch_experiments = cloud_app.list_batch_experiments()
53
+ batch_experiments_dict = [exp.to_dict() for exp in batch_experiments]
54
+
55
+ if output is not None and output != "":
56
+ with open(output, "w") as f:
57
+ json.dump(batch_experiments_dict, f, indent=2)
58
+
59
+ success(msg=f"Batch experiments list saved to [magenta]{output}[/magenta].")
60
+
61
+ return
62
+
63
+ print_json(batch_experiments_dict)
@@ -0,0 +1,66 @@
1
+ """
2
+ This module defines the cloud batch metadata command for the Nextmv CLI.
3
+ """
4
+
5
+ import json
6
+ from typing import Annotated
7
+
8
+ import typer
9
+
10
+ from nextmv.cli.configuration.config import build_app
11
+ from nextmv.cli.message import in_progress, print_json, success
12
+ from nextmv.cli.options import AppIDOption, BatchExperimentIDOption, ProfileOption
13
+
14
+ # Set up subcommand application.
15
+ app = typer.Typer()
16
+
17
+
18
+ @app.command()
19
+ def metadata(
20
+ app_id: AppIDOption,
21
+ batch_experiment_id: BatchExperimentIDOption,
22
+ output: Annotated[
23
+ str | None,
24
+ typer.Option(
25
+ "--output",
26
+ "-o",
27
+ help="Saves the batch experiment metadata to this location.",
28
+ metavar="OUTPUT_PATH",
29
+ ),
30
+ ] = None,
31
+ profile: ProfileOption = None,
32
+ ) -> None:
33
+ """
34
+ Get metadata for a Nextmv Cloud batch experiment.
35
+
36
+ This command retrieves metadata for a specific batch experiment, including
37
+ status, creation date, and other high-level information without the full
38
+ run details.
39
+
40
+ [bold][underline]Examples[/underline][/bold]
41
+
42
+ - Get metadata for batch experiment [magenta]bunny-warren-optimization[/magenta] from application
43
+ [magenta]hare-app[/magenta].
44
+ $ [dim]nextmv cloud batch metadata --app-id hare-app --batch-experiment-id bunny-warren-optimization[/dim]
45
+
46
+ - Get metadata and save to a file.
47
+ $ [dim]nextmv cloud batch metadata --app-id hare-app --batch-experiment-id lettuce-delivery \\
48
+ --output metadata.json[/dim]
49
+
50
+ - Get metadata using a specific profile.
51
+ $ [dim]nextmv cloud batch metadata --app-id hare-app --batch-experiment-id hop-schedule --profile prod[/dim]
52
+ """
53
+
54
+ cloud_app = build_app(app_id=app_id, profile=profile)
55
+ in_progress(msg="Getting batch experiment metadata...")
56
+ batch_metadata = cloud_app.batch_experiment_metadata(batch_id=batch_experiment_id)
57
+ batch_metadata_dict = batch_metadata.to_dict()
58
+
59
+ if output is not None and output != "":
60
+ with open(output, "w") as f:
61
+ json.dump(batch_metadata_dict, f, indent=2)
62
+
63
+ success(msg=f"Batch experiment metadata saved to [magenta]{output}[/magenta].")
64
+ return
65
+
66
+ print_json(batch_metadata_dict)
@@ -0,0 +1,95 @@
1
+ """
2
+ This module defines the cloud batch update command for the Nextmv CLI.
3
+ """
4
+
5
+ import json
6
+ from typing import Annotated
7
+
8
+ import typer
9
+
10
+ from nextmv.cli.configuration.config import build_app
11
+ from nextmv.cli.message import in_progress, print_json, success
12
+ from nextmv.cli.options import AppIDOption, BatchExperimentIDOption, ProfileOption
13
+
14
+ # Set up subcommand application.
15
+ app = typer.Typer()
16
+
17
+
18
+ @app.command()
19
+ def update(
20
+ app_id: AppIDOption,
21
+ batch_experiment_id: BatchExperimentIDOption,
22
+ description: Annotated[
23
+ str | None,
24
+ typer.Option(
25
+ "--description",
26
+ "-d",
27
+ help="Updated description of the batch experiment.",
28
+ metavar="DESCRIPTION",
29
+ ),
30
+ ] = None,
31
+ name: Annotated[
32
+ str | None,
33
+ typer.Option(
34
+ "--name",
35
+ "-n",
36
+ help="Updated name of the batch experiment.",
37
+ metavar="NAME",
38
+ ),
39
+ ] = None,
40
+ output: Annotated[
41
+ str | None,
42
+ typer.Option(
43
+ "--output",
44
+ "-o",
45
+ help="Saves the updated batch experiment information to this location.",
46
+ metavar="OUTPUT_PATH",
47
+ ),
48
+ ] = None,
49
+ profile: ProfileOption = None,
50
+ ) -> None:
51
+ """
52
+ Update a Nextmv Cloud batch experiment.
53
+
54
+ Update the name and/or description of a batch experiment. Any fields not
55
+ specified will remain unchanged.
56
+
57
+ [bold][underline]Examples[/underline][/bold]
58
+
59
+ - Update the name of a batch experiment.
60
+ $ [dim]nextmv cloud batch update --app-id hare-app --batch-experiment-id carrot-feast \\
61
+ --name "Spring Carrot Harvest"[/dim]
62
+
63
+ - Update the description of a batch experiment.
64
+ $ [dim]nextmv cloud batch update --app-id hare-app --batch-experiment-id bunny-hop-routes \\
65
+ --description "Optimizing hop paths through the meadow"[/dim]
66
+
67
+ - Update both name and description and save the result.
68
+ $ [dim]nextmv cloud batch update --app-id hare-app --batch-experiment-id lettuce-delivery \\
69
+ --name "Warren Lettuce Express" --description "Fast lettuce delivery to all burrows" \\
70
+ --output updated-batch.json[/dim]
71
+ """
72
+
73
+ cloud_app = build_app(app_id=app_id, profile=profile)
74
+
75
+ in_progress(msg="Updating batch experiment...")
76
+ batch_experiment = cloud_app.update_batch_experiment(
77
+ batch_experiment_id=batch_experiment_id,
78
+ name=name,
79
+ description=description,
80
+ )
81
+
82
+ batch_experiment_dict = batch_experiment.to_dict()
83
+ success(
84
+ f"Batch experiment [magenta]{batch_experiment_id}[/magenta] updated successfully "
85
+ f"in application [magenta]{app_id}[/magenta]."
86
+ )
87
+
88
+ if output is not None and output != "":
89
+ with open(output, "w") as f:
90
+ json.dump(batch_experiment_dict, f, indent=2)
91
+
92
+ success(msg=f"Updated batch experiment information saved to [magenta]{output}[/magenta].")
93
+ return
94
+
95
+ print_json(batch_experiment_dict)
@@ -0,0 +1,26 @@
1
+ """
2
+ This module defines the cloud data command tree for the Nextmv CLI.
3
+ """
4
+
5
+ import typer
6
+
7
+ from nextmv.cli.cloud.data.upload import app as upload_app
8
+
9
+ # Set up subcommand application.
10
+ app = typer.Typer()
11
+ app.add_typer(upload_app)
12
+
13
+
14
+ @app.callback()
15
+ def callback() -> None:
16
+ """
17
+ Upload data for Nextmv Cloud application components.
18
+
19
+ When data is too large (exceeds [magenta]5 MiB[/magenta]), or you are
20
+ working with the [magenta]multi-file[/magenta] content format, you can use
21
+ this command to upload information to Nextmv Cloud. Requires a pre-signed
22
+ upload URL, which can be obtained using the [code]nextmv cloud upload
23
+ create[/code] command. Use the [magenta].upload_url[/magenta] field from the
24
+ command output.
25
+ """
26
+ pass
@@ -0,0 +1,162 @@
1
+ """
2
+ This module defines the cloud data upload command for the Nextmv CLI.
3
+ """
4
+
5
+ import json
6
+ import sys
7
+ import tarfile
8
+ from pathlib import Path
9
+ from typing import Annotated, Any
10
+
11
+ import typer
12
+
13
+ from nextmv.cli.configuration.config import build_app
14
+ from nextmv.cli.message import error, in_progress, success
15
+ from nextmv.cli.options import AppIDOption, ProfileOption
16
+ from nextmv.cloud.application import Application
17
+
18
+ # Set up subcommand application.
19
+ app = typer.Typer()
20
+
21
+
22
+ @app.command()
23
+ def upload(
24
+ app_id: AppIDOption,
25
+ upload_url: Annotated[
26
+ str,
27
+ typer.Option(
28
+ "--upload-url",
29
+ "-u",
30
+ help="Pre-signed URL for uploading the data.",
31
+ metavar="UPLOAD_URL",
32
+ ),
33
+ ],
34
+ input: Annotated[
35
+ str | None,
36
+ typer.Option(
37
+ "--input",
38
+ "-i",
39
+ help="The input path to use. File or directory depending on content format. "
40
+ "Uses [magenta]stdin[/magenta] if not defined. "
41
+ "Can be a [magenta].tar.gz[/magenta] file for multi-file content format.",
42
+ metavar="INPUT_PATH",
43
+ ),
44
+ ] = None,
45
+ profile: ProfileOption = None,
46
+ ) -> None:
47
+ """
48
+ Upload data for Nextmv Cloud application components.
49
+
50
+ When data is too large, or is not in a text-based content format, you can
51
+ use this command to upload information for a Nextmv Cloud application. Data
52
+ is used for starting new runs, tracking runs, performing experiments, and
53
+ more.
54
+
55
+ The --upload-url flag is required to specify the pre-signed
56
+ upload URL. It can be obtained using the [code]nextmv cloud upload
57
+ create[/code] command. Use the [magenta].upload_url[/magenta] field from
58
+ the command output.
59
+
60
+ The data input should be given through [magenta]stdin[/magenta] or the
61
+ --input flag. When using the --input flag, the value can be one of the
62
+ following:
63
+
64
+ - [yellow]<FILE_PATH>[/yellow]: path to a [magenta]file[/magenta] containing
65
+ the data. Use with the [magenta]json[/magenta], and
66
+ [magenta]text[/magenta] content formats.
67
+ - [yellow]<DIR_PATH>[/yellow]: path to a [magenta]directory[/magenta]
68
+ containing data files. Use with the [magenta]multi-file[/magenta]
69
+ content format.
70
+ - [yellow]<.tar.gz PATH>[/yellow]: path to a [magenta].tar.gz[/magenta] file
71
+ containing tarred data files. Use with the [magenta]multi-file[/magenta]
72
+ content format.
73
+
74
+ [bold][underline]Examples[/underline][/bold]
75
+
76
+ - Upload data from [magenta]stdin[/magenta] for application
77
+ [magenta]hare-app[/magenta].
78
+ $ [dim]echo '{"key": "value"}' | nextmv cloud data upload --app-id hare-app --upload-url <URL>[/dim]
79
+
80
+ - Upload data from a [magenta]JSON[/magenta] file.
81
+ $ [dim]nextmv cloud data upload --app-id hare-app --upload-url <URL> --input data.json[/dim]
82
+
83
+ - Upload data from a [magenta]text[/magenta] file.
84
+ $ [dim]nextmv cloud data upload --app-id hare-app --upload-url <URL> --input data.txt[/dim]
85
+
86
+ - Upload [magenta]multi-file[/magenta] data from a directory.
87
+ $ [dim]nextmv cloud data upload --app-id hare-app --upload-url <URL> --input ./data_directory[/dim]
88
+
89
+ - Upload [magenta]multi-file[/magenta] data from a
90
+ [magenta].tar.gz[/magenta] file.
91
+ $ [dim]nextmv cloud data upload --app-id hare-app --upload-url <URL> --input data.tar.gz[/dim]
92
+
93
+ - Upload data using a specific profile.
94
+ $ [dim]nextmv cloud data upload --app-id hare-app --upload-url <URL> --input data.json \\
95
+ --profile production[/dim]
96
+ """
97
+
98
+ # Validate that input is provided.
99
+ stdin = sys.stdin.read().strip() if sys.stdin.isatty() is False else None
100
+ if stdin is None and (input is None or input == ""):
101
+ error("Input data must be provided via the --input flag or [magenta]stdin[/magenta].")
102
+
103
+ cloud_app = build_app(app_id=app_id, profile=profile)
104
+ data_kwarg = resolve_data_kwarg(
105
+ stdin=stdin,
106
+ input=input,
107
+ cloud_app=cloud_app,
108
+ )
109
+
110
+ in_progress(msg="Uploading data...")
111
+ cloud_app.upload_data(upload_url=upload_url, **data_kwarg)
112
+ success(msg="Data uploaded successfully.")
113
+
114
+
115
+ def resolve_data_kwarg(stdin: str | None, input: str | None, cloud_app: Application) -> dict[str, Any]:
116
+ """
117
+ Gets the keyword argument related to the data that is needed for the
118
+ upload. It handles stdin, file, and directory inputs.
119
+
120
+ Parameters
121
+ ----------
122
+ stdin : str | None
123
+ The stdin input data, if provided.
124
+ input : str | None
125
+ The input path, if provided.
126
+ cloud_app : Application
127
+ The Nextmv Cloud application instance.
128
+
129
+ Returns
130
+ -------
131
+ dict[str, Any]
132
+ The keyword argument with the resolved data.
133
+ """
134
+
135
+ if stdin is not None:
136
+ # Handle the case where stdin is provided as JSON for a JSON app.
137
+ try:
138
+ input_data = json.loads(stdin)
139
+ except json.JSONDecodeError:
140
+ input_data = stdin
141
+
142
+ return {"data": input_data}
143
+
144
+ input_path = Path(input)
145
+
146
+ # If the input is a file, we need to determine if it is a tar file or a
147
+ # regular file. If it is a regular file, we need to read its content.
148
+ if input_path.is_file():
149
+ if tarfile.is_tarfile(input_path):
150
+ return {"tar_file": str(input_path)}
151
+
152
+ input_data = input_path.read_text()
153
+
154
+ return {"data": input_data}
155
+
156
+ # If the input is a directory, we need to tar the contents.
157
+ if input_path.is_dir():
158
+ tar_path = cloud_app._package_inputs(dir_path=str(input_path))
159
+
160
+ return {"tar_file": tar_path}
161
+
162
+ error(f"Input path [magenta]{input}[/magenta] does not exist.")
@@ -0,0 +1,33 @@
1
+ """
2
+ This module defines the cloud ensemble command tree for the Nextmv CLI.
3
+ """
4
+
5
+ import typer
6
+
7
+ from nextmv.cli.cloud.ensemble.create import app as create_app
8
+ from nextmv.cli.cloud.ensemble.delete import app as delete_app
9
+ from nextmv.cli.cloud.ensemble.get import app as get_app
10
+ from nextmv.cli.cloud.ensemble.list import app as list_app
11
+ from nextmv.cli.cloud.ensemble.update import app as update_app
12
+
13
+ # Set up subcommand application.
14
+ app = typer.Typer()
15
+ app.add_typer(create_app)
16
+ app.add_typer(delete_app)
17
+ app.add_typer(get_app)
18
+ app.add_typer(list_app)
19
+ app.add_typer(update_app)
20
+
21
+
22
+ @app.callback()
23
+ def callback() -> None:
24
+ """
25
+ Create and manage Nextmv Cloud ensemble definitions.
26
+
27
+ An ensemble definition defines how to coordinate and execute multiple child
28
+ runs for an application, and how to determine the optimal result from those
29
+ runs. You can configure run groups to specify which instances to run on and
30
+ with what options, as well as evaluation rules to determine the best result
31
+ based on specified metrics.
32
+ """
33
+ pass