nextmv 1.0.0__py3-none-any.whl → 1.0.0.dev0__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.
- nextmv/__about__.py +1 -1
- nextmv/__entrypoint__.py +2 -1
- nextmv/__init__.py +4 -0
- nextmv/cli/CONTRIBUTING.md +40 -112
- nextmv/cli/cloud/__init__.py +0 -4
- nextmv/cli/cloud/acceptance/create.py +22 -20
- nextmv/cli/cloud/acceptance/delete.py +12 -8
- nextmv/cli/cloud/acceptance/get.py +10 -9
- nextmv/cli/cloud/acceptance/list.py +3 -3
- nextmv/cli/cloud/acceptance/update.py +6 -6
- nextmv/cli/cloud/account/__init__.py +3 -3
- nextmv/cli/cloud/account/create.py +11 -11
- nextmv/cli/cloud/account/delete.py +8 -7
- nextmv/cli/cloud/account/get.py +3 -3
- nextmv/cli/cloud/account/update.py +5 -5
- nextmv/cli/cloud/app/create.py +26 -25
- nextmv/cli/cloud/app/delete.py +7 -6
- nextmv/cli/cloud/app/exists.py +2 -2
- nextmv/cli/cloud/app/get.py +2 -2
- nextmv/cli/cloud/app/list.py +3 -3
- nextmv/cli/cloud/app/push.py +54 -349
- nextmv/cli/cloud/app/update.py +12 -12
- nextmv/cli/cloud/batch/create.py +28 -26
- nextmv/cli/cloud/batch/delete.py +10 -6
- nextmv/cli/cloud/batch/get.py +9 -9
- nextmv/cli/cloud/batch/list.py +3 -3
- nextmv/cli/cloud/batch/metadata.py +4 -4
- nextmv/cli/cloud/batch/update.py +6 -6
- nextmv/cli/cloud/data/__init__.py +1 -1
- nextmv/cli/cloud/data/upload.py +15 -15
- nextmv/cli/cloud/ensemble/__init__.py +0 -2
- nextmv/cli/cloud/ensemble/create.py +22 -21
- nextmv/cli/cloud/ensemble/delete.py +10 -6
- nextmv/cli/cloud/ensemble/get.py +4 -4
- nextmv/cli/cloud/ensemble/update.py +9 -9
- nextmv/cli/cloud/input_set/__init__.py +0 -2
- nextmv/cli/cloud/input_set/create.py +22 -22
- nextmv/cli/cloud/input_set/get.py +3 -3
- nextmv/cli/cloud/input_set/list.py +3 -3
- nextmv/cli/cloud/input_set/update.py +24 -24
- nextmv/cli/cloud/instance/create.py +15 -14
- nextmv/cli/cloud/instance/delete.py +7 -6
- nextmv/cli/cloud/instance/exists.py +2 -2
- nextmv/cli/cloud/instance/get.py +2 -2
- nextmv/cli/cloud/instance/list.py +3 -3
- nextmv/cli/cloud/instance/update.py +14 -14
- nextmv/cli/cloud/managed_input/create.py +16 -14
- nextmv/cli/cloud/managed_input/delete.py +8 -7
- nextmv/cli/cloud/managed_input/get.py +3 -3
- nextmv/cli/cloud/managed_input/list.py +3 -3
- nextmv/cli/cloud/managed_input/update.py +9 -9
- nextmv/cli/cloud/run/cancel.py +2 -2
- nextmv/cli/cloud/run/create.py +40 -34
- nextmv/cli/cloud/run/get.py +8 -8
- nextmv/cli/cloud/run/input.py +4 -4
- nextmv/cli/cloud/run/list.py +6 -6
- nextmv/cli/cloud/run/logs.py +10 -9
- nextmv/cli/cloud/run/metadata.py +4 -4
- nextmv/cli/cloud/run/track.py +33 -32
- nextmv/cli/cloud/scenario/create.py +21 -21
- nextmv/cli/cloud/scenario/delete.py +10 -6
- nextmv/cli/cloud/scenario/get.py +9 -9
- nextmv/cli/cloud/scenario/list.py +3 -3
- nextmv/cli/cloud/scenario/metadata.py +4 -4
- nextmv/cli/cloud/scenario/update.py +6 -6
- nextmv/cli/cloud/secrets/create.py +17 -17
- nextmv/cli/cloud/secrets/delete.py +10 -6
- nextmv/cli/cloud/secrets/get.py +4 -4
- nextmv/cli/cloud/secrets/list.py +3 -3
- nextmv/cli/cloud/secrets/update.py +20 -17
- nextmv/cli/cloud/upload/create.py +2 -2
- nextmv/cli/cloud/version/create.py +10 -9
- nextmv/cli/cloud/version/delete.py +7 -6
- nextmv/cli/cloud/version/exists.py +2 -2
- nextmv/cli/cloud/version/get.py +2 -2
- nextmv/cli/cloud/version/list.py +3 -3
- nextmv/cli/cloud/version/update.py +8 -8
- nextmv/cli/community/__init__.py +1 -1
- nextmv/cli/community/clone.py +204 -20
- nextmv/cli/community/list.py +125 -60
- nextmv/cli/configuration/config.py +10 -43
- nextmv/cli/configuration/create.py +7 -7
- nextmv/cli/configuration/delete.py +8 -8
- nextmv/cli/configuration/list.py +3 -3
- nextmv/cli/main.py +36 -26
- nextmv/cli/message.py +54 -71
- nextmv/cli/options.py +0 -28
- nextmv/cli/version.py +1 -1
- nextmv/cloud/__init__.py +38 -14
- nextmv/cloud/acceptance_test.py +65 -1
- nextmv/cloud/account.py +6 -1
- nextmv/cloud/application/__init__.py +75 -18
- nextmv/cloud/application/_acceptance.py +8 -13
- nextmv/cloud/application/_batch_scenario.py +19 -4
- nextmv/cloud/application/_input_set.py +6 -42
- nextmv/cloud/application/_instance.py +3 -3
- nextmv/cloud/application/_managed_input.py +2 -2
- nextmv/cloud/application/_version.py +3 -4
- nextmv/cloud/batch_experiment.py +1 -3
- nextmv/cloud/integration.py +4 -7
- nextmv/deprecated.py +3 -5
- nextmv/input.py +52 -0
- nextmv/local/runner.py +1 -1
- nextmv/model.py +11 -50
- nextmv/options.py +256 -11
- nextmv/output.py +62 -0
- nextmv/run.py +10 -1
- nextmv/status.py +51 -1
- {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/METADATA +4 -5
- nextmv-1.0.0.dev0.dist-info/RECORD +158 -0
- nextmv/cli/cloud/ensemble/list.py +0 -63
- nextmv/cli/cloud/input_set/delete.py +0 -64
- nextmv/cli/cloud/shadow/__init__.py +0 -33
- nextmv/cli/cloud/shadow/create.py +0 -184
- nextmv/cli/cloud/shadow/delete.py +0 -64
- nextmv/cli/cloud/shadow/get.py +0 -61
- nextmv/cli/cloud/shadow/list.py +0 -63
- nextmv/cli/cloud/shadow/metadata.py +0 -66
- nextmv/cli/cloud/shadow/start.py +0 -43
- nextmv/cli/cloud/shadow/stop.py +0 -53
- nextmv/cli/cloud/shadow/update.py +0 -96
- nextmv/cli/cloud/switchback/__init__.py +0 -33
- nextmv/cli/cloud/switchback/create.py +0 -151
- nextmv/cli/cloud/switchback/delete.py +0 -64
- nextmv/cli/cloud/switchback/get.py +0 -62
- nextmv/cli/cloud/switchback/list.py +0 -63
- nextmv/cli/cloud/switchback/metadata.py +0 -68
- nextmv/cli/cloud/switchback/start.py +0 -43
- nextmv/cli/cloud/switchback/stop.py +0 -53
- nextmv/cli/cloud/switchback/update.py +0 -96
- nextmv/cli/confirm.py +0 -34
- nextmv/cloud/application/_shadow.py +0 -320
- nextmv/cloud/application/_switchback.py +0 -332
- nextmv/cloud/community.py +0 -446
- nextmv/cloud/shadow.py +0 -254
- nextmv/cloud/switchback.py +0 -228
- nextmv-1.0.0.dist-info/RECORD +0 -185
- nextmv-1.0.0.dist-info/entry_points.txt +0 -2
- {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/WHEEL +0 -0
- {nextmv-1.0.0.dist-info → nextmv-1.0.0.dev0.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,7 +14,7 @@ from nextmv.cli.configuration.config import (
|
|
|
14
14
|
obscure_api_key,
|
|
15
15
|
save_config,
|
|
16
16
|
)
|
|
17
|
-
from nextmv.cli.message import error,
|
|
17
|
+
from nextmv.cli.message import error, info, success
|
|
18
18
|
|
|
19
19
|
# Set up subcommand application.
|
|
20
20
|
app = typer.Typer()
|
|
@@ -58,14 +58,14 @@ def create(
|
|
|
58
58
|
[bold][underline]Examples[/underline][/bold]
|
|
59
59
|
|
|
60
60
|
- Default configuration.
|
|
61
|
-
$ [
|
|
61
|
+
$ [green]nextmv configuration create --api-key NEXTMV_API_KEY[/green]
|
|
62
62
|
|
|
63
63
|
- Configure a profile named [magenta]hare[/magenta].
|
|
64
|
-
$ [
|
|
64
|
+
$ [green]nextmv configuration create --api-key NEXTMV_API_KEY --profile hare[/green]
|
|
65
65
|
"""
|
|
66
66
|
|
|
67
67
|
if profile is not None and profile.strip().lower() == "default":
|
|
68
|
-
error("[
|
|
68
|
+
error("[code]default[/code] is a reserved profile name.")
|
|
69
69
|
|
|
70
70
|
endpoint = str(endpoint)
|
|
71
71
|
if endpoint.startswith("https://"):
|
|
@@ -88,7 +88,7 @@ def create(
|
|
|
88
88
|
save_config(config)
|
|
89
89
|
|
|
90
90
|
success("Configuration saved successfully.")
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
info(f"\t[bold]Profile[/bold]: {profile or 'Default'}")
|
|
92
|
+
info(f"\t[bold]API Key[/bold]: {obscure_api_key(api_key)}")
|
|
93
93
|
if endpoint != DEFAULT_ENDPOINT:
|
|
94
|
-
|
|
94
|
+
info(f"\t[bold]Endpoint[/bold]: {endpoint}")
|
|
@@ -5,9 +5,9 @@ This module defines the configuration delete command for the Nextmv CLI.
|
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
|
+
from rich.prompt import Confirm
|
|
8
9
|
|
|
9
10
|
from nextmv.cli.configuration.config import load_config, save_config
|
|
10
|
-
from nextmv.cli.confirm import get_confirmation
|
|
11
11
|
from nextmv.cli.message import error, info, success
|
|
12
12
|
|
|
13
13
|
# Set up subcommand application.
|
|
@@ -36,29 +36,29 @@ def delete(
|
|
|
36
36
|
] = False,
|
|
37
37
|
) -> None:
|
|
38
38
|
"""
|
|
39
|
-
Delete a profile from the configuration.
|
|
40
|
-
|
|
41
|
-
Use the --yes flag to skip the confirmation prompt.
|
|
39
|
+
Delete a profile from the configuration. Use the [code]--yes[/code]
|
|
40
|
+
flag to skip the confirmation prompt.
|
|
42
41
|
|
|
43
42
|
[bold][underline]Examples[/underline][/bold]
|
|
44
43
|
|
|
45
44
|
- Delete a profile named [magenta]hare[/magenta].
|
|
46
|
-
$ [
|
|
45
|
+
$ [green]nextmv configuration delete --profile hare[/green]
|
|
47
46
|
|
|
48
47
|
- Delete a profile named [magenta]hare[/magenta] without confirmation prompt.
|
|
49
|
-
$ [
|
|
48
|
+
$ [green]nextmv configuration delete --profile hare --yes[/green]
|
|
50
49
|
"""
|
|
51
50
|
config = load_config()
|
|
52
51
|
if profile not in config:
|
|
53
52
|
error(f"Profile [magenta]{profile}[/magenta] does not exist.")
|
|
54
53
|
|
|
55
54
|
if not yes:
|
|
56
|
-
confirm =
|
|
55
|
+
confirm = Confirm.ask(
|
|
57
56
|
f"Are you sure you want to delete profile [magenta]{profile}[/magenta]? This action cannot be undone.",
|
|
57
|
+
default=False,
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
if not confirm:
|
|
61
|
-
info(f"Profile [magenta]{profile}[/magenta] will not be deleted.")
|
|
61
|
+
info(msg=f"Profile [magenta]{profile}[/magenta] will not be deleted.", emoji=":bulb:")
|
|
62
62
|
return
|
|
63
63
|
|
|
64
64
|
del config[profile]
|
nextmv/cli/configuration/list.py
CHANGED
|
@@ -6,7 +6,7 @@ import typer
|
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.table import Table
|
|
8
8
|
|
|
9
|
-
from nextmv.cli.configuration.config import API_KEY_KEY, ENDPOINT_KEY, load_config,
|
|
9
|
+
from nextmv.cli.configuration.config import API_KEY_KEY, ENDPOINT_KEY, load_config, obscure_api_key
|
|
10
10
|
from nextmv.cli.message import error
|
|
11
11
|
|
|
12
12
|
# Set up subcommand application.
|
|
@@ -22,7 +22,7 @@ def list() -> None:
|
|
|
22
22
|
[bold][underline]Examples[/underline][/bold]
|
|
23
23
|
|
|
24
24
|
- Show current configuration and all profiles.
|
|
25
|
-
$ [
|
|
25
|
+
$ [green]nextmv configuration list[/green]
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
config = load_config()
|
|
@@ -38,7 +38,7 @@ def list() -> None:
|
|
|
38
38
|
|
|
39
39
|
for k, v in config.items():
|
|
40
40
|
# Skip default configuration.
|
|
41
|
-
if k in
|
|
41
|
+
if k in {API_KEY_KEY, ENDPOINT_KEY}:
|
|
42
42
|
continue
|
|
43
43
|
|
|
44
44
|
profile = {
|
nextmv/cli/main.py
CHANGED
|
@@ -13,25 +13,22 @@ about the features used here. An example of Rich markup can be found in the
|
|
|
13
13
|
epilog of the Typer application defined below.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
import os
|
|
16
17
|
import sys
|
|
17
18
|
from typing import Annotated
|
|
18
19
|
|
|
19
20
|
import rich
|
|
20
21
|
import typer
|
|
21
|
-
from
|
|
22
|
+
from rich.prompt import Confirm
|
|
22
23
|
|
|
23
24
|
from nextmv.cli.cloud import app as cloud_app
|
|
24
25
|
from nextmv.cli.community import app as community_app
|
|
25
26
|
from nextmv.cli.configuration import app as configuration_app
|
|
26
27
|
from nextmv.cli.configuration.config import CONFIG_DIR, GO_CLI_PATH, load_config
|
|
27
|
-
from nextmv.cli.confirm import get_confirmation
|
|
28
28
|
from nextmv.cli.message import error, info, success, warning
|
|
29
29
|
from nextmv.cli.version import app as version_app
|
|
30
30
|
from nextmv.cli.version import version_callback
|
|
31
31
|
|
|
32
|
-
# Disable dim text for the extended help of commands.
|
|
33
|
-
rich_utils.STYLE_HELPTEXT = ""
|
|
34
|
-
|
|
35
32
|
# Main CLI application.
|
|
36
33
|
app = typer.Typer(
|
|
37
34
|
help="The Nextmv Command Line Interface (CLI).",
|
|
@@ -69,15 +66,6 @@ def callback(
|
|
|
69
66
|
environment.
|
|
70
67
|
"""
|
|
71
68
|
|
|
72
|
-
# Skip checks for help commands.
|
|
73
|
-
if "--help" in sys.argv or "-h" in sys.argv:
|
|
74
|
-
return
|
|
75
|
-
|
|
76
|
-
# Skip checks for certain commands.
|
|
77
|
-
ignored_commands = {"configuration", "version"}
|
|
78
|
-
if ctx.invoked_subcommand in ignored_commands:
|
|
79
|
-
return
|
|
80
|
-
|
|
81
69
|
handle_go_cli()
|
|
82
70
|
handle_config_existence(ctx)
|
|
83
71
|
|
|
@@ -92,20 +80,19 @@ def handle_go_cli() -> None:
|
|
|
92
80
|
|
|
93
81
|
exists = go_cli_exists()
|
|
94
82
|
if exists:
|
|
95
|
-
delete =
|
|
83
|
+
delete = Confirm.ask(
|
|
96
84
|
"Do you want to delete the [italic red]deprecated[/italic red] Nextmv CLI "
|
|
97
|
-
f"at [magenta]{GO_CLI_PATH}[/magenta] now?"
|
|
85
|
+
f"at [magenta]{GO_CLI_PATH}[/magenta] now?",
|
|
86
|
+
default=False,
|
|
98
87
|
)
|
|
99
88
|
if delete:
|
|
100
89
|
remove_go_cli()
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
f"by removing references to [magenta]{CONFIG_DIR}[/magenta] from it."
|
|
108
|
-
)
|
|
90
|
+
else:
|
|
91
|
+
info(
|
|
92
|
+
msg="You can delete the [italic red]deprecated[/italic red] Nextmv CLI later by removing "
|
|
93
|
+
f"[magenta]{GO_CLI_PATH}[/magenta]. Make sure you also clean up your [code]PATH[/code].",
|
|
94
|
+
emoji=":bulb:",
|
|
95
|
+
)
|
|
109
96
|
|
|
110
97
|
|
|
111
98
|
def handle_config_existence(ctx: typer.Context) -> None:
|
|
@@ -118,6 +105,10 @@ def handle_config_existence(ctx: typer.Context) -> None:
|
|
|
118
105
|
The Typer context object.
|
|
119
106
|
"""
|
|
120
107
|
|
|
108
|
+
ignored_commands = {"configuration", "version"}
|
|
109
|
+
if ctx.invoked_subcommand in ignored_commands:
|
|
110
|
+
return
|
|
111
|
+
|
|
121
112
|
config = load_config()
|
|
122
113
|
if config == {}:
|
|
123
114
|
error("No configuration found. Please run [code]nextmv configuration create[/code].")
|
|
@@ -139,9 +130,11 @@ def go_cli_exists() -> bool:
|
|
|
139
130
|
if exists:
|
|
140
131
|
warning(
|
|
141
132
|
"A [italic red]deprecated[/italic red] Nextmv CLI is installed at "
|
|
142
|
-
f"[magenta]{GO_CLI_PATH}[/magenta]. You
|
|
133
|
+
f"[magenta]{GO_CLI_PATH}[/magenta]. You must delete it to avoid conflicts."
|
|
143
134
|
)
|
|
144
135
|
|
|
136
|
+
check_config_in_path()
|
|
137
|
+
|
|
145
138
|
return exists
|
|
146
139
|
|
|
147
140
|
|
|
@@ -152,7 +145,24 @@ def remove_go_cli() -> None:
|
|
|
152
145
|
|
|
153
146
|
if GO_CLI_PATH.exists():
|
|
154
147
|
GO_CLI_PATH.unlink()
|
|
155
|
-
success(f"Deleted
|
|
148
|
+
success(f"Deleted deprecated [magenta]{GO_CLI_PATH}[/magenta].")
|
|
149
|
+
|
|
150
|
+
check_config_in_path()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def check_config_in_path() -> None:
|
|
154
|
+
"""
|
|
155
|
+
Check if the configuration directory is in the PATH and notify the user.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
path_dirs = os.environ.get("PATH", "").split(os.pathsep)
|
|
159
|
+
config_dir_str = str(CONFIG_DIR)
|
|
160
|
+
|
|
161
|
+
if config_dir_str in path_dirs:
|
|
162
|
+
warning(
|
|
163
|
+
f"[magenta]{CONFIG_DIR}[/magenta] was found in your [code]PATH[/code]. "
|
|
164
|
+
f"You should remove any entries related to [magenta]{CONFIG_DIR}[/magenta] from your [code]PATH[/code]."
|
|
165
|
+
)
|
|
156
166
|
|
|
157
167
|
|
|
158
168
|
def main() -> None:
|
nextmv/cli/message.py
CHANGED
|
@@ -11,107 +11,106 @@ import rich
|
|
|
11
11
|
import typer
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def error(msg: str) -> None:
|
|
15
15
|
"""
|
|
16
|
-
Pretty-print
|
|
17
|
-
|
|
18
|
-
string as specified in:
|
|
19
|
-
https://rich.readthedocs.io/en/latest/markup.html#emoji.
|
|
16
|
+
Pretty-print an error message and exit with code 1. Your message should end
|
|
17
|
+
with a period.
|
|
20
18
|
|
|
21
19
|
Parameters
|
|
22
20
|
----------
|
|
23
21
|
msg : str
|
|
24
|
-
The message to display.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
The error message to display.
|
|
23
|
+
|
|
24
|
+
Raises
|
|
25
|
+
------
|
|
26
|
+
typer.Exit
|
|
27
|
+
Exits the program with code 1.
|
|
30
28
|
"""
|
|
31
29
|
|
|
32
|
-
msg =
|
|
33
|
-
if
|
|
34
|
-
|
|
35
|
-
return
|
|
30
|
+
msg = msg.rstrip("\n")
|
|
31
|
+
if not msg.endswith("."):
|
|
32
|
+
msg += "."
|
|
36
33
|
|
|
37
|
-
rich.print(msg, file=sys.stderr)
|
|
34
|
+
rich.print(f"[red]Error:[/red] {msg}", file=sys.stderr)
|
|
35
|
+
|
|
36
|
+
raise typer.Exit(code=1)
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
def
|
|
39
|
+
def success(msg: str) -> None:
|
|
41
40
|
"""
|
|
42
|
-
Pretty-print
|
|
43
|
-
period.
|
|
41
|
+
Pretty-print a success message. Your message should end with a period.
|
|
44
42
|
|
|
45
43
|
Parameters
|
|
46
44
|
----------
|
|
47
45
|
msg : str
|
|
48
|
-
The
|
|
46
|
+
The success message to display.
|
|
49
47
|
"""
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
msg = msg.rstrip("\n")
|
|
50
|
+
if not msg.endswith("."):
|
|
51
|
+
msg += "."
|
|
52
52
|
|
|
53
|
+
rich.print(f":white_check_mark: {msg}", file=sys.stderr)
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
|
|
56
|
+
def warning(msg: str) -> None:
|
|
55
57
|
"""
|
|
56
|
-
Pretty-print
|
|
57
|
-
should end with a period.
|
|
58
|
+
Pretty-print a warning message. Your message should end with a period.
|
|
58
59
|
|
|
59
60
|
Parameters
|
|
60
61
|
----------
|
|
61
62
|
msg : str
|
|
62
|
-
The
|
|
63
|
+
The warning message to display.
|
|
63
64
|
"""
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
msg = msg.rstrip("\n")
|
|
67
|
+
if not msg.endswith("."):
|
|
68
|
+
msg += "."
|
|
66
69
|
|
|
70
|
+
rich.print(f":construction: {msg}", file=sys.stderr)
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
|
|
73
|
+
def info(msg: str, emoji: str | None = None) -> None:
|
|
69
74
|
"""
|
|
70
|
-
Pretty-print
|
|
75
|
+
Pretty-print an informational message. Your message should end with a
|
|
76
|
+
period. The use of emojis is encouraged to give context to the message. An
|
|
77
|
+
emoji should be a string as specified in:
|
|
78
|
+
https://rich.readthedocs.io/en/latest/markup.html#emoji.
|
|
71
79
|
|
|
72
80
|
Parameters
|
|
73
81
|
----------
|
|
74
82
|
msg : str
|
|
75
|
-
The
|
|
83
|
+
The informational message to display.
|
|
84
|
+
emoji : str | None
|
|
85
|
+
An optional emoji to prefix the message. If None, no emoji is used. The
|
|
86
|
+
emoji should be a string as specified in:
|
|
87
|
+
https://rich.readthedocs.io/en/latest/markup.html#emoji. For example:
|
|
88
|
+
`:hourglass_flowing_sand:`.
|
|
76
89
|
"""
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def warning(msg: str) -> None:
|
|
82
|
-
"""
|
|
83
|
-
Pretty-print a warning message. Your message should end with a period.
|
|
91
|
+
msg = msg.rstrip("\n")
|
|
92
|
+
if not msg.endswith("."):
|
|
93
|
+
msg += "."
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
The warning message to display.
|
|
89
|
-
"""
|
|
95
|
+
if emoji:
|
|
96
|
+
rich.print(f"{emoji} {msg}", file=sys.stderr)
|
|
97
|
+
return
|
|
90
98
|
|
|
91
|
-
msg =
|
|
92
|
-
rich.print(f":construction: [yellow] Warning:[/yellow] {msg}", file=sys.stderr)
|
|
99
|
+
rich.print(msg, file=sys.stderr)
|
|
93
100
|
|
|
94
101
|
|
|
95
|
-
def
|
|
102
|
+
def in_progress(msg: str) -> None:
|
|
96
103
|
"""
|
|
97
|
-
Pretty-print an
|
|
98
|
-
with a period.
|
|
104
|
+
Pretty-print an in-progress message with an hourglass emoji. Your message
|
|
105
|
+
should end with a period.
|
|
99
106
|
|
|
100
107
|
Parameters
|
|
101
108
|
----------
|
|
102
109
|
msg : str
|
|
103
|
-
The
|
|
104
|
-
|
|
105
|
-
Raises
|
|
106
|
-
------
|
|
107
|
-
typer.Exit
|
|
108
|
-
Exits the program with code 1.
|
|
110
|
+
The in-progress message to display.
|
|
109
111
|
"""
|
|
110
112
|
|
|
111
|
-
msg =
|
|
112
|
-
rich.print(f":x: [red]Error:[/red] {msg}", file=sys.stderr)
|
|
113
|
-
|
|
114
|
-
raise typer.Exit(code=1)
|
|
113
|
+
info(msg, emoji=":hourglass_flowing_sand:")
|
|
115
114
|
|
|
116
115
|
|
|
117
116
|
def print_json(data: dict[str, Any] | list[dict[str, Any]]) -> None:
|
|
@@ -152,19 +151,3 @@ def enum_values(enum_class: Enum) -> str:
|
|
|
152
151
|
return " and ".join(values)
|
|
153
152
|
|
|
154
153
|
return ", ".join(values[:-1]) + ", and " + values[-1]
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def _format(msg: str) -> str:
|
|
158
|
-
"""
|
|
159
|
-
Format a message to ensure it ends with a period.
|
|
160
|
-
|
|
161
|
-
Parameters
|
|
162
|
-
----------
|
|
163
|
-
msg : str
|
|
164
|
-
The message to format.
|
|
165
|
-
"""
|
|
166
|
-
msg = msg.rstrip("\n")
|
|
167
|
-
if not msg.endswith("."):
|
|
168
|
-
msg += "."
|
|
169
|
-
|
|
170
|
-
return msg
|
nextmv/cli/options.py
CHANGED
|
@@ -190,31 +190,3 @@ SecretsCollectionIDOption = Annotated[
|
|
|
190
190
|
metavar="SECRETS_COLLECTION_ID",
|
|
191
191
|
),
|
|
192
192
|
]
|
|
193
|
-
|
|
194
|
-
# shadow_test_id option - can be used in any command that requires a shadow test ID.
|
|
195
|
-
# Define it as follows in commands or callbacks, as necessary:
|
|
196
|
-
# shadow_test_id: ShadowTestIDOption
|
|
197
|
-
ShadowTestIDOption = Annotated[
|
|
198
|
-
str,
|
|
199
|
-
typer.Option(
|
|
200
|
-
"--shadow-test-id",
|
|
201
|
-
"-s",
|
|
202
|
-
help="The Nextmv Cloud shadow test ID to use for this action.",
|
|
203
|
-
envvar="NEXTMV_SHADOW_TEST_ID",
|
|
204
|
-
metavar="SHADOW_TEST_ID",
|
|
205
|
-
),
|
|
206
|
-
]
|
|
207
|
-
|
|
208
|
-
# switchback_test_id option - can be used in any command that requires a switchback test ID.
|
|
209
|
-
# Define it as follows in commands or callbacks, as necessary:
|
|
210
|
-
# switchback_test_id: SwitchbackTestIDOption
|
|
211
|
-
SwitchbackTestIDOption = Annotated[
|
|
212
|
-
str,
|
|
213
|
-
typer.Option(
|
|
214
|
-
"--switchback-test-id",
|
|
215
|
-
"-s",
|
|
216
|
-
help="The Nextmv Cloud switchback test ID to use for this action.",
|
|
217
|
-
envvar="NEXTMV_SWITCHBACK_TEST_ID",
|
|
218
|
-
metavar="SWITCHBACK_TEST_ID",
|
|
219
|
-
),
|
|
220
|
-
]
|
nextmv/cli/version.py
CHANGED
nextmv/cloud/__init__.py
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
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
|
+
|
|
3
40
|
from .acceptance_test import AcceptanceTest as AcceptanceTest
|
|
4
41
|
from .acceptance_test import AcceptanceTestResults as AcceptanceTestResults
|
|
5
42
|
from .acceptance_test import Comparison as Comparison
|
|
@@ -15,6 +52,7 @@ from .acceptance_test import MetricToleranceType as MetricToleranceType
|
|
|
15
52
|
from .acceptance_test import MetricType as MetricType
|
|
16
53
|
from .acceptance_test import ResultStatistics as ResultStatistics
|
|
17
54
|
from .acceptance_test import StatisticType as StatisticType
|
|
55
|
+
from .acceptance_test import ToleranceType as ToleranceType
|
|
18
56
|
from .account import Account as Account
|
|
19
57
|
from .account import AccountMember as AccountMember
|
|
20
58
|
from .account import Queue as Queue
|
|
@@ -30,9 +68,6 @@ from .batch_experiment import BatchExperimentRun as BatchExperimentRun
|
|
|
30
68
|
from .batch_experiment import ExperimentStatus as ExperimentStatus
|
|
31
69
|
from .client import Client as Client
|
|
32
70
|
from .client import get_size as get_size
|
|
33
|
-
from .community import CommunityApp as CommunityApp
|
|
34
|
-
from .community import clone_community_app as clone_community_app
|
|
35
|
-
from .community import list_community_apps as list_community_apps
|
|
36
71
|
from .ensemble import EnsembleDefinition as EnsembleDefinition
|
|
37
72
|
from .ensemble import EvaluationRule as EvaluationRule
|
|
38
73
|
from .ensemble import RuleObjective as RuleObjective
|
|
@@ -55,17 +90,6 @@ from .secrets import Secret as Secret
|
|
|
55
90
|
from .secrets import SecretsCollection as SecretsCollection
|
|
56
91
|
from .secrets import SecretsCollectionSummary as SecretsCollectionSummary
|
|
57
92
|
from .secrets import SecretType as SecretType
|
|
58
|
-
from .shadow import ShadowTest as ShadowTest
|
|
59
|
-
from .shadow import ShadowTestMetadata as ShadowTestMetadata
|
|
60
|
-
from .shadow import StartEvents as StartEvents
|
|
61
|
-
from .shadow import StopIntent as StopIntent
|
|
62
|
-
from .shadow import TerminationEvents as TerminationEvents
|
|
63
|
-
from .shadow import TestComparison as TestComparison
|
|
64
|
-
from .switchback import SwitchbackPlan as SwitchbackPlan
|
|
65
|
-
from .switchback import SwitchbackPlanUnit as SwitchbackPlanUnit
|
|
66
|
-
from .switchback import SwitchbackTest as SwitchbackTest
|
|
67
|
-
from .switchback import SwitchbackTestMetadata as SwitchbackTestMetadata
|
|
68
|
-
from .switchback import TestComparisonSingle as TestComparisonSingle
|
|
69
93
|
from .url import DownloadURL as DownloadURL
|
|
70
94
|
from .url import UploadURL as UploadURL
|
|
71
95
|
from .version import Version as Version
|
nextmv/cloud/acceptance_test.py
CHANGED
|
@@ -13,7 +13,7 @@ StatisticType : Enum
|
|
|
13
13
|
Type of statistical process for collapsing multiple values of a metric.
|
|
14
14
|
Comparison : Enum
|
|
15
15
|
Comparison operators to use for comparing two metrics.
|
|
16
|
-
|
|
16
|
+
ToleranceType : Enum
|
|
17
17
|
Type of tolerance used for a metric.
|
|
18
18
|
ExperimentStatus : Enum
|
|
19
19
|
Status of an acceptance test experiment.
|
|
@@ -46,6 +46,7 @@ from enum import Enum
|
|
|
46
46
|
|
|
47
47
|
from nextmv.base_model import BaseModel
|
|
48
48
|
from nextmv.cloud.batch_experiment import ExperimentStatus
|
|
49
|
+
from nextmv.deprecated import deprecated
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
class MetricType(str, Enum):
|
|
@@ -211,6 +212,69 @@ class Comparison(str, Enum):
|
|
|
211
212
|
"""Not equal to metric type."""
|
|
212
213
|
|
|
213
214
|
|
|
215
|
+
class ToleranceType(str, Enum):
|
|
216
|
+
"""
|
|
217
|
+
!!! warning
|
|
218
|
+
`ToleranceType` is deprecated, use `MetricToleranceType` instead.
|
|
219
|
+
|
|
220
|
+
Type of tolerance used for a metric.
|
|
221
|
+
|
|
222
|
+
You can import the `ToleranceType` class directly from `cloud`:
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from nextmv.cloud import ToleranceType
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
This enumeration defines the different types of tolerances that can be used
|
|
229
|
+
when comparing metrics in acceptance tests.
|
|
230
|
+
|
|
231
|
+
Attributes
|
|
232
|
+
----------
|
|
233
|
+
undefined : str
|
|
234
|
+
Undefined tolerance type (empty string).
|
|
235
|
+
absolute : str
|
|
236
|
+
Absolute tolerance type, using a fixed value.
|
|
237
|
+
relative : str
|
|
238
|
+
Relative tolerance type, using a percentage.
|
|
239
|
+
|
|
240
|
+
Examples
|
|
241
|
+
--------
|
|
242
|
+
>>> from nextmv.cloud import ToleranceType
|
|
243
|
+
>>> tol_type = ToleranceType.absolute
|
|
244
|
+
>>> tol_type
|
|
245
|
+
<ToleranceType.absolute: 'absolute'>
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
undefined = ""
|
|
249
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
250
|
+
Undefined tolerance type."""
|
|
251
|
+
absolute = "absolute"
|
|
252
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
253
|
+
Absolute tolerance type."""
|
|
254
|
+
relative = "relative"
|
|
255
|
+
"""ToleranceType is deprecated, please use MetricToleranceType instead.
|
|
256
|
+
Relative tolerance type."""
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# Override __getattribute__ to emit deprecation warnings when enum values are accessed
|
|
260
|
+
_original_getattribute = ToleranceType.__class__.__getattribute__
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _deprecated_getattribute(cls, name: str):
|
|
264
|
+
# Only emit deprecation warning if this is specifically the ToleranceType class
|
|
265
|
+
if cls is ToleranceType and name in ("undefined", "absolute", "relative"):
|
|
266
|
+
deprecated(
|
|
267
|
+
f"ToleranceType.{name}",
|
|
268
|
+
"ToleranceType is deprecated and will be removed in a future version. "
|
|
269
|
+
"Please use MetricToleranceType instead",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return _original_getattribute(cls, name)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
ToleranceType.__class__.__getattribute__ = _deprecated_getattribute
|
|
276
|
+
|
|
277
|
+
|
|
214
278
|
class MetricToleranceType(str, Enum):
|
|
215
279
|
"""
|
|
216
280
|
Type of tolerance used for a metric.
|
nextmv/cloud/account.py
CHANGED
|
@@ -21,7 +21,7 @@ from pydantic import AliasChoices, Field
|
|
|
21
21
|
|
|
22
22
|
from nextmv.base_model import BaseModel
|
|
23
23
|
from nextmv.cloud.client import Client
|
|
24
|
-
from nextmv.status import StatusV2
|
|
24
|
+
from nextmv.status import Status, StatusV2
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class QueuedRun(BaseModel):
|
|
@@ -57,6 +57,8 @@ class QueuedRun(BaseModel):
|
|
|
57
57
|
ID of the application version used for the run.
|
|
58
58
|
execution_class : str
|
|
59
59
|
Execution class used for the run.
|
|
60
|
+
status : Status
|
|
61
|
+
Deprecated: use status_v2.
|
|
60
62
|
status_v2 : StatusV2
|
|
61
63
|
Status of the run.
|
|
62
64
|
|
|
@@ -72,6 +74,7 @@ class QueuedRun(BaseModel):
|
|
|
72
74
|
... "application_instance_id": "appins-123456",
|
|
73
75
|
... "application_version_id": "appver-123456",
|
|
74
76
|
... "execution_class": "standard",
|
|
77
|
+
... "status": "RUNNING",
|
|
75
78
|
... "status_v2": "RUNNING"
|
|
76
79
|
... })
|
|
77
80
|
>>> print(queued_run.name)
|
|
@@ -96,6 +99,8 @@ class QueuedRun(BaseModel):
|
|
|
96
99
|
"""ID of the application version used for the run."""
|
|
97
100
|
execution_class: str
|
|
98
101
|
"""Execution class used for the run."""
|
|
102
|
+
status: Status
|
|
103
|
+
"""Deprecated: use status_v2."""
|
|
99
104
|
status_v2: StatusV2
|
|
100
105
|
"""Status of the run."""
|
|
101
106
|
|