snowflake-cli-labs 3.0.0rc1__py3-none-any.whl → 3.0.0rc3__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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/cli_app.py +10 -1
- snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -0
- snowflake/cli/_app/secret.py +9 -0
- snowflake/cli/_app/snow_connector.py +110 -51
- snowflake/cli/_app/telemetry.py +8 -4
- snowflake/cli/_app/version_check.py +74 -0
- snowflake/cli/_plugins/git/commands.py +55 -14
- snowflake/cli/_plugins/git/manager.py +53 -7
- snowflake/cli/_plugins/helpers/commands.py +57 -0
- snowflake/cli/{api/commands/typer_pre_execute.py → _plugins/helpers/plugin_spec.py} +14 -10
- snowflake/cli/_plugins/nativeapp/application_entity.py +651 -0
- snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_entity_model.py +2 -2
- snowflake/cli/_plugins/nativeapp/application_package_entity.py +1107 -0
- snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_package_entity_model.py +3 -3
- snowflake/cli/_plugins/nativeapp/artifacts.py +10 -9
- snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +3 -6
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +50 -32
- snowflake/cli/_plugins/nativeapp/commands.py +84 -16
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -9
- snowflake/cli/_plugins/nativeapp/manager.py +56 -92
- snowflake/cli/_plugins/nativeapp/policy.py +3 -0
- snowflake/cli/_plugins/nativeapp/project_model.py +2 -2
- snowflake/cli/_plugins/nativeapp/run_processor.py +65 -272
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +70 -0
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +11 -154
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +150 -40
- snowflake/cli/_plugins/nativeapp/version/commands.py +6 -24
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +35 -235
- snowflake/cli/_plugins/snowpark/commands.py +5 -5
- snowflake/cli/_plugins/snowpark/common.py +4 -4
- snowflake/cli/_plugins/snowpark/models.py +2 -1
- snowflake/cli/{api/entities → _plugins/snowpark}/snowpark_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities/snowpark_entity.py → _plugins/snowpark/snowpark_entity_model.py} +3 -6
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +1 -1
- snowflake/cli/_plugins/stage/manager.py +9 -4
- snowflake/cli/_plugins/streamlit/commands.py +4 -4
- snowflake/cli/_plugins/streamlit/manager.py +17 -4
- snowflake/cli/{api/entities → _plugins/streamlit}/streamlit_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities → _plugins/streamlit}/streamlit_entity_model.py +5 -12
- snowflake/cli/_plugins/workspace/action_context.py +2 -1
- snowflake/cli/_plugins/workspace/commands.py +127 -48
- snowflake/cli/_plugins/workspace/manager.py +1 -0
- snowflake/cli/_plugins/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +136 -313
- snowflake/cli/api/commands/flags.py +76 -91
- snowflake/cli/api/commands/snow_typer.py +7 -5
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/connections.py +214 -0
- snowflake/cli/api/console/abc.py +4 -2
- snowflake/cli/api/entities/common.py +4 -0
- snowflake/cli/api/entities/utils.py +41 -31
- snowflake/cli/api/errno.py +1 -0
- snowflake/cli/api/identifiers.py +7 -3
- snowflake/cli/api/project/definition.py +11 -0
- snowflake/cli/api/project/definition_conversion.py +175 -16
- snowflake/cli/api/project/schemas/entities/common.py +15 -14
- snowflake/cli/api/project/schemas/entities/entities.py +13 -10
- snowflake/cli/api/project/schemas/project_definition.py +107 -45
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +0 -7
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
- snowflake/cli/api/rendering/project_definition_templates.py +4 -0
- snowflake/cli/api/rendering/sql_templates.py +7 -0
- snowflake/cli/api/sql_execution.py +6 -15
- snowflake/cli/api/utils/definition_rendering.py +3 -1
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/METADATA +9 -9
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/RECORD +88 -81
- snowflake/cli/api/entities/application_entity.py +0 -12
- snowflake/cli/api/entities/application_package_entity.py +0 -553
- snowflake/cli/api/project/schemas/snowpark/__init__.py +0 -13
- snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
- /snowflake/cli/{api/project/schemas/native_app → _plugins/helpers}/__init__.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
- /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from snowflake.cli._plugins.nativeapp.constants import (
|
|
4
|
+
ALLOWED_SPECIAL_COMMENTS,
|
|
5
|
+
COMMENT_COL,
|
|
6
|
+
)
|
|
7
|
+
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
8
|
+
ApplicationCreatedExternallyError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# from snowflake.cli._plugins.nativeapp.project_model import NativeAppProjectModel
|
|
12
|
+
from snowflake.cli._plugins.stage.manager import StageManager
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SameAccountInstallMethod:
|
|
16
|
+
_requires_created_by_cli: bool
|
|
17
|
+
_from_release_directive: bool
|
|
18
|
+
version: Optional[str]
|
|
19
|
+
patch: Optional[int]
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
requires_created_by_cli: bool,
|
|
24
|
+
version: Optional[str] = None,
|
|
25
|
+
patch: Optional[int] = None,
|
|
26
|
+
from_release_directive: bool = False,
|
|
27
|
+
):
|
|
28
|
+
self._requires_created_by_cli = requires_created_by_cli
|
|
29
|
+
self.version = version
|
|
30
|
+
self.patch = patch
|
|
31
|
+
self._from_release_directive = from_release_directive
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def unversioned_dev(cls):
|
|
35
|
+
"""aka. stage dev aka loose files"""
|
|
36
|
+
return cls(True)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def versioned_dev(cls, version: str, patch: Optional[int] = None):
|
|
40
|
+
return cls(False, version, patch)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def release_directive(cls):
|
|
44
|
+
return cls(False, from_release_directive=True)
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def is_dev_mode(self) -> bool:
|
|
48
|
+
return not self._from_release_directive
|
|
49
|
+
|
|
50
|
+
def using_clause(
|
|
51
|
+
self,
|
|
52
|
+
stage_fqn: str,
|
|
53
|
+
) -> str:
|
|
54
|
+
if self._from_release_directive:
|
|
55
|
+
return ""
|
|
56
|
+
|
|
57
|
+
if self.version:
|
|
58
|
+
patch_clause = f"patch {self.patch}" if self.patch else ""
|
|
59
|
+
return f"using version {self.version} {patch_clause}"
|
|
60
|
+
|
|
61
|
+
stage_name = StageManager.quote_stage_name(stage_fqn)
|
|
62
|
+
return f"using {stage_name}"
|
|
63
|
+
|
|
64
|
+
def ensure_app_usable(self, app_name: str, app_role: str, show_app_row: dict):
|
|
65
|
+
"""Raise an exception if we cannot proceed with install given the pre-existing application object"""
|
|
66
|
+
|
|
67
|
+
if self._requires_created_by_cli:
|
|
68
|
+
if show_app_row[COMMENT_COL] not in ALLOWED_SPECIAL_COMMENTS:
|
|
69
|
+
# this application object was not created by this tooling
|
|
70
|
+
raise ApplicationCreatedExternallyError(app_name)
|
|
@@ -15,179 +15,36 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
|
-
from textwrap import dedent
|
|
19
18
|
from typing import Dict, Optional
|
|
20
19
|
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
from snowflake.cli._plugins.nativeapp.application_entity import (
|
|
21
|
+
ApplicationEntity,
|
|
22
|
+
)
|
|
23
|
+
from snowflake.cli._plugins.nativeapp.application_package_entity import (
|
|
24
|
+
ApplicationPackageEntity,
|
|
26
25
|
)
|
|
27
26
|
from snowflake.cli._plugins.nativeapp.manager import (
|
|
28
27
|
NativeAppCommandProcessor,
|
|
29
28
|
NativeAppManager,
|
|
30
29
|
)
|
|
31
|
-
from snowflake.cli._plugins.nativeapp.utils import (
|
|
32
|
-
needs_confirmation,
|
|
33
|
-
)
|
|
34
30
|
from snowflake.cli.api.console import cli_console as cc
|
|
35
|
-
from snowflake.cli.api.entities.application_package_entity import (
|
|
36
|
-
ApplicationPackageEntity,
|
|
37
|
-
)
|
|
38
|
-
from snowflake.cli.api.entities.utils import (
|
|
39
|
-
drop_generic_object,
|
|
40
|
-
ensure_correct_owner,
|
|
41
|
-
)
|
|
42
|
-
from snowflake.cli.api.errno import APPLICATION_NO_LONGER_AVAILABLE
|
|
43
|
-
from snowflake.connector import ProgrammingError
|
|
44
31
|
|
|
45
32
|
|
|
46
33
|
class NativeAppTeardownProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
47
34
|
def __init__(self, project_definition: Dict, project_root: Path):
|
|
48
35
|
super().__init__(project_definition, project_root)
|
|
49
36
|
|
|
50
|
-
def drop_generic_object(
|
|
51
|
-
self, object_type: str, object_name: str, role: str, cascade: bool = False
|
|
52
|
-
):
|
|
53
|
-
return drop_generic_object(
|
|
54
|
-
console=cc,
|
|
55
|
-
object_type=object_type,
|
|
56
|
-
object_name=object_name,
|
|
57
|
-
role=role,
|
|
58
|
-
cascade=cascade,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
37
|
def drop_application(
|
|
62
38
|
self, auto_yes: bool, interactive: bool = False, cascade: Optional[bool] = None
|
|
63
39
|
):
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# 1. If existing application is not found, exit gracefully
|
|
71
|
-
show_obj_row = self.get_existing_app_info()
|
|
72
|
-
if show_obj_row is None:
|
|
73
|
-
cc.warning(
|
|
74
|
-
f"Role {self.app_role} does not own any application object with the name {self.app_name}, or the application object does not exist."
|
|
75
|
-
)
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
# 2. Check for the right owner
|
|
79
|
-
ensure_correct_owner(
|
|
80
|
-
row=show_obj_row, role=self.app_role, obj_name=self.app_name
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# 3. Check if created by the Snowflake CLI
|
|
84
|
-
row_comment = show_obj_row[COMMENT_COL]
|
|
85
|
-
if row_comment not in ALLOWED_SPECIAL_COMMENTS and needs_confirmation(
|
|
86
|
-
needs_confirm, auto_yes
|
|
87
|
-
):
|
|
88
|
-
should_drop_object = typer.confirm(
|
|
89
|
-
dedent(
|
|
90
|
-
f"""\
|
|
91
|
-
Application object {self.app_name} was not created by Snowflake CLI.
|
|
92
|
-
Application object details:
|
|
93
|
-
Name: {self.app_name}
|
|
94
|
-
Created on: {show_obj_row["created_on"]}
|
|
95
|
-
Source: {show_obj_row["source"]}
|
|
96
|
-
Owner: {show_obj_row[OWNER_COL]}
|
|
97
|
-
Comment: {show_obj_row[COMMENT_COL]}
|
|
98
|
-
Version: {show_obj_row["version"]}
|
|
99
|
-
Patch: {show_obj_row["patch"]}
|
|
100
|
-
Are you sure you want to drop it?
|
|
101
|
-
"""
|
|
102
|
-
)
|
|
103
|
-
)
|
|
104
|
-
if not should_drop_object:
|
|
105
|
-
cc.message(f"Did not drop application object {self.app_name}.")
|
|
106
|
-
# The user desires to keep the app, therefore we can't proceed since it would
|
|
107
|
-
# leave behind an orphan app when we get to dropping the package
|
|
108
|
-
raise typer.Abort()
|
|
109
|
-
|
|
110
|
-
# 4. Check for application objects owned by the application
|
|
111
|
-
# This query will fail if the application package has already been dropped, so handle this case gracefully
|
|
112
|
-
has_objects_to_drop = False
|
|
113
|
-
message_prefix = ""
|
|
114
|
-
cascade_true_message = ""
|
|
115
|
-
cascade_false_message = ""
|
|
116
|
-
interactive_prompt = ""
|
|
117
|
-
non_interactive_abort = ""
|
|
118
|
-
try:
|
|
119
|
-
if application_objects := self.get_objects_owned_by_application():
|
|
120
|
-
has_objects_to_drop = True
|
|
121
|
-
message_prefix = (
|
|
122
|
-
f"The following objects are owned by application {self.app_name}"
|
|
123
|
-
)
|
|
124
|
-
cascade_true_message = f"{message_prefix} and will be dropped:"
|
|
125
|
-
cascade_false_message = f"{message_prefix} and will NOT be dropped:"
|
|
126
|
-
interactive_prompt = "Would you like to drop these objects in addition to the application? [y/n/ABORT]"
|
|
127
|
-
non_interactive_abort = "Re-run teardown again with --cascade or --no-cascade to specify whether these objects should be dropped along with the application"
|
|
128
|
-
except ProgrammingError as e:
|
|
129
|
-
if e.errno != APPLICATION_NO_LONGER_AVAILABLE:
|
|
130
|
-
raise
|
|
131
|
-
application_objects = []
|
|
132
|
-
message_prefix = f"Could not determine which objects are owned by application {self.app_name}"
|
|
133
|
-
has_objects_to_drop = True # potentially, but we don't know what they are
|
|
134
|
-
cascade_true_message = (
|
|
135
|
-
f"{message_prefix}, an unknown number of objects will be dropped."
|
|
136
|
-
)
|
|
137
|
-
cascade_false_message = f"{message_prefix}, they will NOT be dropped."
|
|
138
|
-
interactive_prompt = f"Would you like to drop an unknown set of objects in addition to the application? [y/n/ABORT]"
|
|
139
|
-
non_interactive_abort = f"Re-run teardown again with --cascade or --no-cascade to specify whether any objects should be dropped along with the application."
|
|
140
|
-
|
|
141
|
-
if has_objects_to_drop:
|
|
142
|
-
if cascade is True:
|
|
143
|
-
# If the user explicitly passed the --cascade flag
|
|
144
|
-
cc.message(cascade_true_message)
|
|
145
|
-
with cc.indented():
|
|
146
|
-
for obj in application_objects:
|
|
147
|
-
cc.message(self._application_object_to_str(obj))
|
|
148
|
-
elif cascade is False:
|
|
149
|
-
# If the user explicitly passed the --no-cascade flag
|
|
150
|
-
cc.message(cascade_false_message)
|
|
151
|
-
with cc.indented():
|
|
152
|
-
for obj in application_objects:
|
|
153
|
-
cc.message(self._application_object_to_str(obj))
|
|
154
|
-
elif interactive:
|
|
155
|
-
# If the user didn't pass any cascade flag and the session is interactive
|
|
156
|
-
cc.message(message_prefix)
|
|
157
|
-
with cc.indented():
|
|
158
|
-
for obj in application_objects:
|
|
159
|
-
cc.message(self._application_object_to_str(obj))
|
|
160
|
-
user_response = typer.prompt(
|
|
161
|
-
interactive_prompt,
|
|
162
|
-
show_default=False,
|
|
163
|
-
default="ABORT",
|
|
164
|
-
).lower()
|
|
165
|
-
if user_response in ["y", "yes"]:
|
|
166
|
-
cascade = True
|
|
167
|
-
elif user_response in ["n", "no"]:
|
|
168
|
-
cascade = False
|
|
169
|
-
else:
|
|
170
|
-
raise typer.Abort()
|
|
171
|
-
else:
|
|
172
|
-
# Else abort since we don't know what to do and can't ask the user
|
|
173
|
-
cc.message(message_prefix)
|
|
174
|
-
with cc.indented():
|
|
175
|
-
for obj in application_objects:
|
|
176
|
-
cc.message(self._application_object_to_str(obj))
|
|
177
|
-
cc.message(non_interactive_abort)
|
|
178
|
-
raise typer.Abort()
|
|
179
|
-
elif cascade is None:
|
|
180
|
-
# If there's nothing to drop, set cascade to an explicit False value
|
|
181
|
-
cascade = False
|
|
182
|
-
|
|
183
|
-
# 5. All validations have passed, drop object
|
|
184
|
-
self.drop_generic_object(
|
|
185
|
-
object_type="application",
|
|
186
|
-
object_name=self.app_name,
|
|
187
|
-
role=self.app_role,
|
|
40
|
+
return ApplicationEntity.drop(
|
|
41
|
+
console=cc,
|
|
42
|
+
app_name=self.app_name,
|
|
43
|
+
app_role=self.app_role,
|
|
44
|
+
auto_yes=auto_yes,
|
|
45
|
+
interactive=interactive,
|
|
188
46
|
cascade=cascade,
|
|
189
47
|
)
|
|
190
|
-
return # The application object was successfully dropped, therefore exit gracefully
|
|
191
48
|
|
|
192
49
|
def drop_package(self, auto_yes: bool):
|
|
193
50
|
return ApplicationPackageEntity.drop(
|
|
@@ -14,25 +14,29 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import inspect
|
|
17
18
|
from functools import wraps
|
|
18
|
-
from typing import Any, Dict, Optional, Union
|
|
19
|
+
from typing import Any, Dict, Optional, Type, TypeVar, Union
|
|
19
20
|
|
|
21
|
+
import typer
|
|
20
22
|
from click import ClickException
|
|
21
|
-
from snowflake.cli.
|
|
22
|
-
get_cli_context,
|
|
23
|
-
get_cli_context_manager,
|
|
24
|
-
)
|
|
25
|
-
from snowflake.cli.api.project.schemas.entities.application_entity_model import (
|
|
23
|
+
from snowflake.cli._plugins.nativeapp.application_entity_model import (
|
|
26
24
|
ApplicationEntityModel,
|
|
27
25
|
)
|
|
28
|
-
from snowflake.cli.
|
|
26
|
+
from snowflake.cli._plugins.nativeapp.application_package_entity_model import (
|
|
29
27
|
ApplicationPackageEntityModel,
|
|
30
28
|
)
|
|
31
|
-
from snowflake.cli.api.
|
|
29
|
+
from snowflake.cli.api.cli_global_context import (
|
|
30
|
+
get_cli_context,
|
|
31
|
+
get_cli_context_manager,
|
|
32
|
+
)
|
|
33
|
+
from snowflake.cli.api.commands.decorators import _options_decorator_factory
|
|
34
|
+
from snowflake.cli.api.project.schemas.entities.common import EntityModelBase
|
|
32
35
|
from snowflake.cli.api.project.schemas.project_definition import (
|
|
33
36
|
DefinitionV11,
|
|
34
37
|
DefinitionV20,
|
|
35
38
|
)
|
|
39
|
+
from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
|
|
36
40
|
from snowflake.cli.api.utils.definition_rendering import render_definition_template
|
|
37
41
|
|
|
38
42
|
|
|
@@ -48,29 +52,53 @@ def _convert_v2_artifact_to_v1_dict(
|
|
|
48
52
|
return v2_artifact
|
|
49
53
|
|
|
50
54
|
|
|
51
|
-
def _pdf_v2_to_v1(
|
|
55
|
+
def _pdf_v2_to_v1(
|
|
56
|
+
v2_definition: DefinitionV20,
|
|
57
|
+
package_entity_id: str = "",
|
|
58
|
+
app_entity_id: str = "",
|
|
59
|
+
app_required: bool = False,
|
|
60
|
+
) -> DefinitionV11:
|
|
52
61
|
pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
app_definition
|
|
63
|
+
# Determine the application entity to convert, there can be zero or one
|
|
64
|
+
app_definition = find_entity(
|
|
65
|
+
v2_definition,
|
|
66
|
+
ApplicationEntityModel,
|
|
67
|
+
app_entity_id,
|
|
68
|
+
disambiguation_option="--app-entity-id",
|
|
69
|
+
required=app_required,
|
|
70
|
+
)
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if app_definition:
|
|
72
|
+
# Infer or verify the package if we have an app entity to convert
|
|
73
|
+
if app_definition:
|
|
74
|
+
target_package = app_definition.from_.target
|
|
75
|
+
if package_entity_id:
|
|
76
|
+
# If the user specified a package entity ID,
|
|
77
|
+
# check that the app entity targets the user-specified package entity
|
|
78
|
+
# if the app entity is used by the command being run
|
|
79
|
+
if target_package != package_entity_id and app_required:
|
|
66
80
|
raise ClickException(
|
|
67
|
-
"
|
|
81
|
+
f"The application entity {app_definition.entity_id} does not "
|
|
82
|
+
f"target the application package entity {package_entity_id}. Either"
|
|
83
|
+
f"use --package-entity-id {target_package} to target the correct package entity, "
|
|
84
|
+
f"or omit the --package-entity-id flag to automatically use the package entity "
|
|
85
|
+
f"that the application entity targets."
|
|
68
86
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
elif target_package in v2_definition.get_entities_by_type(
|
|
88
|
+
ApplicationPackageEntityModel.get_type()
|
|
89
|
+
):
|
|
90
|
+
# If the user didn't target a specific package entity, use the one the app entity targets
|
|
91
|
+
package_entity_id = target_package
|
|
92
|
+
|
|
93
|
+
# Determine the package entity to convert, there must be one
|
|
94
|
+
app_package_definition = find_entity(
|
|
95
|
+
v2_definition,
|
|
96
|
+
ApplicationPackageEntityModel,
|
|
97
|
+
package_entity_id,
|
|
98
|
+
disambiguation_option="--package-entity-id",
|
|
99
|
+
required=True,
|
|
100
|
+
)
|
|
101
|
+
assert app_package_definition is not None # satisfy mypy
|
|
74
102
|
|
|
75
103
|
# NativeApp
|
|
76
104
|
if app_definition and app_definition.fqn.identifier:
|
|
@@ -130,7 +158,60 @@ def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
|
|
|
130
158
|
return result.project_definition
|
|
131
159
|
|
|
132
160
|
|
|
133
|
-
|
|
161
|
+
T = TypeVar("T", bound=EntityModelBase)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def find_entity(
|
|
165
|
+
project_definition: DefinitionV20,
|
|
166
|
+
entity_class: Type[T],
|
|
167
|
+
entity_id: str,
|
|
168
|
+
disambiguation_option: str,
|
|
169
|
+
required: bool,
|
|
170
|
+
) -> T | None:
|
|
171
|
+
"""
|
|
172
|
+
Find an entity of the specified type in the project definition file.
|
|
173
|
+
|
|
174
|
+
If an ID is passed, only that entity will be considered,
|
|
175
|
+
otherwise look for a single entity of the specified type.
|
|
176
|
+
|
|
177
|
+
If there are multiple entities of the specified type,
|
|
178
|
+
the user must specify which one to use using the CLI option
|
|
179
|
+
named in the disambiguation_option parameter.
|
|
180
|
+
|
|
181
|
+
If no entity is found, an error is raised if required is True,
|
|
182
|
+
otherwise None is returned.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
entity_type = entity_class.get_type()
|
|
186
|
+
entities = project_definition.get_entities_by_type(entity_type)
|
|
187
|
+
|
|
188
|
+
entity: Optional[T] = None
|
|
189
|
+
|
|
190
|
+
if entity_id:
|
|
191
|
+
# If we're looking for a specific entity, use that one directly
|
|
192
|
+
entity = entities.get(entity_id)
|
|
193
|
+
elif len(entities) == 1:
|
|
194
|
+
# Otherwise, if there is only one entity, fall back to that one
|
|
195
|
+
entity = next(iter(entities.values()))
|
|
196
|
+
elif len(entities) > 1 and required:
|
|
197
|
+
# If there are multiple entities and it's required,
|
|
198
|
+
# the user must specify which one to use
|
|
199
|
+
raise ClickException(
|
|
200
|
+
f"More than one {entity_type} entity exists in the project definition file, "
|
|
201
|
+
f"specify {disambiguation_option} to choose which one to operate on."
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# If we don't have a package entity to convert, error out if it's required
|
|
205
|
+
if not entity and required:
|
|
206
|
+
with_id = f'with ID "{entity_id}" ' if entity_id else ""
|
|
207
|
+
raise ClickException(
|
|
208
|
+
f"Could not find an {entity_type} entity {with_id}in the project definition file."
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
return entity
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def nativeapp_definition_v2_to_v1(*, app_required: bool = False):
|
|
134
215
|
"""
|
|
135
216
|
A command decorator that attempts to automatically convert a native app project from
|
|
136
217
|
definition v2 to v1.1. Assumes with_project_definition() has already been called.
|
|
@@ -139,16 +220,45 @@ def nativeapp_definition_v2_to_v1(func):
|
|
|
139
220
|
entity type is expected.
|
|
140
221
|
"""
|
|
141
222
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
223
|
+
def decorator(func):
|
|
224
|
+
@wraps(func)
|
|
225
|
+
def wrapper(*args, **kwargs):
|
|
226
|
+
original_pdf: Optional[DefinitionV20] = get_cli_context().project_definition
|
|
227
|
+
if not original_pdf:
|
|
228
|
+
raise ValueError(
|
|
229
|
+
"Project definition could not be found. The nativeapp_definition_v2_to_v1 command decorator assumes with_project_definition() was called before it."
|
|
230
|
+
)
|
|
231
|
+
if original_pdf.definition_version == "2":
|
|
232
|
+
package_entity_id = kwargs.get("package_entity_id", "")
|
|
233
|
+
app_entity_id = kwargs.get("app_entity_id", "")
|
|
234
|
+
pdfv1 = _pdf_v2_to_v1(
|
|
235
|
+
original_pdf, package_entity_id, app_entity_id, app_required
|
|
236
|
+
)
|
|
237
|
+
get_cli_context_manager().override_project_definition = pdfv1
|
|
238
|
+
return func(*args, **kwargs)
|
|
239
|
+
|
|
240
|
+
return _options_decorator_factory(
|
|
241
|
+
wrapper,
|
|
242
|
+
additional_options=[
|
|
243
|
+
inspect.Parameter(
|
|
244
|
+
"package_entity_id",
|
|
245
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
246
|
+
annotation=Optional[str],
|
|
247
|
+
default=typer.Option(
|
|
248
|
+
default="",
|
|
249
|
+
help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
|
|
250
|
+
),
|
|
251
|
+
),
|
|
252
|
+
inspect.Parameter(
|
|
253
|
+
"app_entity_id",
|
|
254
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
255
|
+
annotation=Optional[str],
|
|
256
|
+
default=typer.Option(
|
|
257
|
+
default="",
|
|
258
|
+
help="The ID of the application entity on which to operate when definition_version is 2 or higher.",
|
|
259
|
+
),
|
|
260
|
+
),
|
|
261
|
+
],
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return decorator
|
|
@@ -51,7 +51,7 @@ log = logging.getLogger(__name__)
|
|
|
51
51
|
|
|
52
52
|
@app.command(requires_connection=True)
|
|
53
53
|
@with_project_definition()
|
|
54
|
-
@nativeapp_definition_v2_to_v1
|
|
54
|
+
@nativeapp_definition_v2_to_v1()
|
|
55
55
|
def create(
|
|
56
56
|
version: Optional[str] = typer.Argument(
|
|
57
57
|
None,
|
|
@@ -82,42 +82,24 @@ def create(
|
|
|
82
82
|
if version is None and patch is not None:
|
|
83
83
|
raise MissingParameter("Cannot provide a patch without version!")
|
|
84
84
|
|
|
85
|
-
is_interactive = False
|
|
86
|
-
if force:
|
|
87
|
-
policy = AllowAlwaysPolicy()
|
|
88
|
-
elif interactive:
|
|
89
|
-
is_interactive = True
|
|
90
|
-
policy = AskAlwaysPolicy()
|
|
91
|
-
else:
|
|
92
|
-
policy = DenyAlwaysPolicy()
|
|
93
|
-
|
|
94
|
-
if skip_git_check:
|
|
95
|
-
git_policy = DenyAlwaysPolicy()
|
|
96
|
-
else:
|
|
97
|
-
git_policy = AllowAlwaysPolicy()
|
|
98
|
-
|
|
99
85
|
cli_context = get_cli_context()
|
|
100
86
|
processor = NativeAppVersionCreateProcessor(
|
|
101
87
|
project_definition=cli_context.project_definition.native_app,
|
|
102
88
|
project_root=cli_context.project_root,
|
|
103
89
|
)
|
|
104
|
-
|
|
105
|
-
# We need build_bundle() to (optionally) find version in manifest.yml and create an application package
|
|
106
|
-
bundle_map = processor.build_bundle()
|
|
107
90
|
processor.process(
|
|
108
|
-
bundle_map=bundle_map,
|
|
109
91
|
version=version,
|
|
110
92
|
patch=patch,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
93
|
+
force=force,
|
|
94
|
+
interactive=interactive,
|
|
95
|
+
skip_git_check=skip_git_check,
|
|
114
96
|
)
|
|
115
97
|
return MessageResult(f"Version create is now complete.")
|
|
116
98
|
|
|
117
99
|
|
|
118
100
|
@app.command("list", requires_connection=True)
|
|
119
101
|
@with_project_definition()
|
|
120
|
-
@nativeapp_definition_v2_to_v1
|
|
102
|
+
@nativeapp_definition_v2_to_v1()
|
|
121
103
|
def version_list(
|
|
122
104
|
**options,
|
|
123
105
|
) -> CommandResult:
|
|
@@ -138,7 +120,7 @@ def version_list(
|
|
|
138
120
|
|
|
139
121
|
@app.command(requires_connection=True)
|
|
140
122
|
@with_project_definition()
|
|
141
|
-
@nativeapp_definition_v2_to_v1
|
|
123
|
+
@nativeapp_definition_v2_to_v1()
|
|
142
124
|
def drop(
|
|
143
125
|
version: Optional[str] = typer.Argument(
|
|
144
126
|
None,
|