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
|
@@ -9,11 +9,9 @@ from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
|
9
9
|
BundleMap,
|
|
10
10
|
resolve_without_follow,
|
|
11
11
|
)
|
|
12
|
-
from snowflake.cli._plugins.nativeapp.constants import OWNER_COL
|
|
13
12
|
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
14
13
|
InvalidTemplateInFileError,
|
|
15
14
|
MissingScriptError,
|
|
16
|
-
UnexpectedOwnerError,
|
|
17
15
|
)
|
|
18
16
|
from snowflake.cli._plugins.nativeapp.utils import verify_exists, verify_no_directories
|
|
19
17
|
from snowflake.cli._plugins.stage.diff import (
|
|
@@ -34,12 +32,12 @@ from snowflake.cli.api.errno import (
|
|
|
34
32
|
)
|
|
35
33
|
from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
|
|
36
34
|
from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
|
|
37
|
-
from snowflake.cli.api.project.util import unquote_identifier
|
|
38
35
|
from snowflake.cli.api.rendering.sql_templates import (
|
|
39
36
|
choose_sql_jinja_env_based_on_template_syntax,
|
|
40
37
|
)
|
|
41
38
|
from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
|
|
42
39
|
from snowflake.connector import ProgrammingError
|
|
40
|
+
from snowflake.connector.cursor import SnowflakeCursor
|
|
43
41
|
|
|
44
42
|
|
|
45
43
|
def generic_sql_error_handler(
|
|
@@ -79,17 +77,6 @@ def generic_sql_error_handler(
|
|
|
79
77
|
raise err
|
|
80
78
|
|
|
81
79
|
|
|
82
|
-
def ensure_correct_owner(row: dict, role: str, obj_name: str) -> None:
|
|
83
|
-
"""
|
|
84
|
-
Check if an object has the right owner role
|
|
85
|
-
"""
|
|
86
|
-
actual_owner = row[
|
|
87
|
-
OWNER_COL
|
|
88
|
-
].upper() # Because unquote_identifier() always returns uppercase str
|
|
89
|
-
if actual_owner != unquote_identifier(role):
|
|
90
|
-
raise UnexpectedOwnerError(obj_name, role, actual_owner)
|
|
91
|
-
|
|
92
|
-
|
|
93
80
|
def _get_stage_paths_to_sync(
|
|
94
81
|
local_paths_to_sync: List[Path], deploy_root: Path
|
|
95
82
|
) -> List[StagePath]:
|
|
@@ -303,27 +290,34 @@ def render_script_templates(
|
|
|
303
290
|
- List of rendered scripts content
|
|
304
291
|
Size of the return list is the same as the size of the input scripts list.
|
|
305
292
|
"""
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
template_content = script_full_path.read_text(file_size_limit_mb=UNLIMITED)
|
|
311
|
-
env = override_env or choose_sql_jinja_env_based_on_template_syntax(
|
|
312
|
-
template_content, reference_name=relpath
|
|
313
|
-
)
|
|
314
|
-
result = env.from_string(template_content).render(jinja_context)
|
|
315
|
-
scripts_contents.append(result)
|
|
293
|
+
return [
|
|
294
|
+
render_script_template(project_root, jinja_context, script, override_env)
|
|
295
|
+
for script in scripts
|
|
296
|
+
]
|
|
316
297
|
|
|
317
|
-
except FileNotFoundError as e:
|
|
318
|
-
raise MissingScriptError(relpath) from e
|
|
319
298
|
|
|
320
|
-
|
|
321
|
-
|
|
299
|
+
def render_script_template(
|
|
300
|
+
project_root: Path,
|
|
301
|
+
jinja_context: dict[str, Any],
|
|
302
|
+
script: str,
|
|
303
|
+
override_env: Optional[jinja2.Environment] = None,
|
|
304
|
+
) -> str:
|
|
305
|
+
script_full_path = SecurePath(project_root) / script
|
|
306
|
+
try:
|
|
307
|
+
template_content = script_full_path.read_text(file_size_limit_mb=UNLIMITED)
|
|
308
|
+
env = override_env or choose_sql_jinja_env_based_on_template_syntax(
|
|
309
|
+
template_content, reference_name=script
|
|
310
|
+
)
|
|
311
|
+
return env.from_string(template_content).render(jinja_context)
|
|
312
|
+
|
|
313
|
+
except FileNotFoundError as e:
|
|
314
|
+
raise MissingScriptError(script) from e
|
|
322
315
|
|
|
323
|
-
|
|
324
|
-
|
|
316
|
+
except jinja2.TemplateSyntaxError as e:
|
|
317
|
+
raise InvalidTemplateInFileError(script, e, e.lineno) from e
|
|
325
318
|
|
|
326
|
-
|
|
319
|
+
except jinja2.UndefinedError as e:
|
|
320
|
+
raise InvalidTemplateInFileError(script, e) from e
|
|
327
321
|
|
|
328
322
|
|
|
329
323
|
def validation_item_to_str(item: dict[str, str | int]):
|
|
@@ -355,3 +349,19 @@ def drop_generic_object(
|
|
|
355
349
|
raise SnowflakeSQLExecutionError(drop_query)
|
|
356
350
|
|
|
357
351
|
console.message(f"Dropped {object_type} {object_name} successfully.")
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def print_messages(
|
|
355
|
+
console: AbstractConsole, create_or_upgrade_cursor: Optional[SnowflakeCursor]
|
|
356
|
+
):
|
|
357
|
+
"""
|
|
358
|
+
Shows messages in the console returned by the CREATE or UPGRADE
|
|
359
|
+
APPLICATION command.
|
|
360
|
+
"""
|
|
361
|
+
if not create_or_upgrade_cursor:
|
|
362
|
+
return
|
|
363
|
+
|
|
364
|
+
messages = [row[0] for row in create_or_upgrade_cursor.fetchall()]
|
|
365
|
+
for message in messages:
|
|
366
|
+
console.warning(message)
|
|
367
|
+
console.message("")
|
snowflake/cli/api/errno.py
CHANGED
snowflake/cli/api/identifiers.py
CHANGED
|
@@ -18,8 +18,7 @@ import re
|
|
|
18
18
|
|
|
19
19
|
from click import ClickException
|
|
20
20
|
from snowflake.cli.api.exceptions import FQNInconsistencyError, FQNNameError
|
|
21
|
-
from snowflake.cli.api.project.schemas.identifier_model import (
|
|
22
|
-
Identifier,
|
|
21
|
+
from snowflake.cli.api.project.schemas.v1.identifier_model import (
|
|
23
22
|
ObjectIdentifierBaseModel,
|
|
24
23
|
)
|
|
25
24
|
from snowflake.cli.api.project.util import VALID_IDENTIFIER_REGEX, identifier_for_url
|
|
@@ -142,8 +141,10 @@ class FQN:
|
|
|
142
141
|
return fqn.set_database(model.database).set_schema(model.schema_name)
|
|
143
142
|
|
|
144
143
|
@classmethod
|
|
145
|
-
def from_identifier_model_v2(cls, model
|
|
144
|
+
def from_identifier_model_v2(cls, model) -> "FQN":
|
|
146
145
|
"""Create an instance from object model."""
|
|
146
|
+
from snowflake.cli.api.project.schemas.entities.common import Identifier
|
|
147
|
+
|
|
147
148
|
if not isinstance(model, Identifier):
|
|
148
149
|
raise ClickException(f"Expected {type(Identifier).__name__}, got {model}.")
|
|
149
150
|
|
|
@@ -184,3 +185,6 @@ class FQN:
|
|
|
184
185
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
185
186
|
|
|
186
187
|
return self.using_connection(get_cli_context().connection)
|
|
188
|
+
|
|
189
|
+
def to_dict(self) -> dict:
|
|
190
|
+
return {"name": self.name, "schema": self.schema, "database": self.database}
|
|
@@ -23,6 +23,7 @@ from snowflake.cli.api.cli_global_context import get_cli_context
|
|
|
23
23
|
from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
|
|
24
24
|
from snowflake.cli.api.project.schemas.project_definition import (
|
|
25
25
|
ProjectProperties,
|
|
26
|
+
YamlOverride,
|
|
26
27
|
)
|
|
27
28
|
from snowflake.cli.api.project.util import (
|
|
28
29
|
append_to_identifier,
|
|
@@ -37,6 +38,7 @@ from snowflake.cli.api.utils.definition_rendering import (
|
|
|
37
38
|
)
|
|
38
39
|
from snowflake.cli.api.utils.dict_utils import deep_merge_dicts
|
|
39
40
|
from snowflake.cli.api.utils.types import Context, Definition
|
|
41
|
+
from yaml import MappingNode, SequenceNode
|
|
40
42
|
|
|
41
43
|
DEFAULT_USERNAME = "unknown_user"
|
|
42
44
|
|
|
@@ -50,6 +52,7 @@ def _get_merged_definitions(paths: List[Path]) -> Optional[Definition]:
|
|
|
50
52
|
loader.add_constructor(
|
|
51
53
|
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, _no_duplicates_constructor
|
|
52
54
|
)
|
|
55
|
+
loader.add_constructor("!override", _override_tag)
|
|
53
56
|
|
|
54
57
|
with spaths[0].open("r", read_file_limit_mb=DEFAULT_SIZE_LIMIT_MB) as base_yml:
|
|
55
58
|
definition = yaml.load(base_yml.read(), Loader=loader) or {}
|
|
@@ -113,3 +116,11 @@ def _no_duplicates_constructor(loader, node, deep=False):
|
|
|
113
116
|
)
|
|
114
117
|
mapping[key] = value
|
|
115
118
|
return loader.construct_mapping(node, deep)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _override_tag(loader, node, deep=False):
|
|
122
|
+
if isinstance(node, SequenceNode):
|
|
123
|
+
return YamlOverride(data=loader.construct_sequence(node, deep))
|
|
124
|
+
if isinstance(node, MappingNode):
|
|
125
|
+
return YamlOverride(data=loader.construct_mapping(node, deep))
|
|
126
|
+
return node.value
|
|
@@ -1,42 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import logging
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
from typing import Any, Dict, Literal, Optional
|
|
4
6
|
|
|
5
7
|
from click import ClickException
|
|
8
|
+
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
9
|
+
build_bundle,
|
|
10
|
+
)
|
|
6
11
|
from snowflake.cli._plugins.snowpark.common import is_name_a_templated_one
|
|
7
12
|
from snowflake.cli.api.constants import (
|
|
8
13
|
DEFAULT_ENV_FILE,
|
|
9
14
|
DEFAULT_PAGES_DIR,
|
|
15
|
+
PROJECT_TEMPLATE_VARIABLE_CLOSING,
|
|
10
16
|
PROJECT_TEMPLATE_VARIABLE_OPENING,
|
|
11
17
|
SNOWPARK_SHARED_MIXIN,
|
|
12
18
|
)
|
|
19
|
+
from snowflake.cli.api.entities.utils import render_script_template
|
|
20
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
21
|
+
SqlScriptHookType,
|
|
22
|
+
)
|
|
13
23
|
from snowflake.cli.api.project.schemas.project_definition import (
|
|
14
24
|
ProjectDefinition,
|
|
15
25
|
ProjectDefinitionV2,
|
|
16
26
|
)
|
|
17
|
-
from snowflake.cli.api.project.schemas.
|
|
27
|
+
from snowflake.cli.api.project.schemas.v1.native_app.application import (
|
|
28
|
+
Application,
|
|
29
|
+
ApplicationV11,
|
|
30
|
+
)
|
|
31
|
+
from snowflake.cli.api.project.schemas.v1.native_app.native_app import NativeApp
|
|
32
|
+
from snowflake.cli.api.project.schemas.v1.native_app.package import Package, PackageV11
|
|
33
|
+
from snowflake.cli.api.project.schemas.v1.snowpark.callable import (
|
|
18
34
|
FunctionSchema,
|
|
19
35
|
ProcedureSchema,
|
|
20
36
|
)
|
|
21
|
-
from snowflake.cli.api.project.schemas.snowpark.snowpark import Snowpark
|
|
22
|
-
from snowflake.cli.api.project.schemas.streamlit.streamlit import Streamlit
|
|
37
|
+
from snowflake.cli.api.project.schemas.v1.snowpark.snowpark import Snowpark
|
|
38
|
+
from snowflake.cli.api.project.schemas.v1.streamlit.streamlit import Streamlit
|
|
39
|
+
from snowflake.cli.api.rendering.jinja import get_basic_jinja_env
|
|
23
40
|
|
|
24
41
|
log = logging.getLogger(__name__)
|
|
25
42
|
|
|
26
43
|
|
|
27
44
|
def convert_project_definition_to_v2(
|
|
28
|
-
pd: ProjectDefinition, accept_templates: bool = False
|
|
45
|
+
project_root: Path, pd: ProjectDefinition, accept_templates: bool = False
|
|
29
46
|
) -> ProjectDefinitionV2:
|
|
30
47
|
_check_if_project_definition_meets_requirements(pd, accept_templates)
|
|
31
48
|
|
|
32
49
|
snowpark_data = convert_snowpark_to_v2_data(pd.snowpark) if pd.snowpark else {}
|
|
33
50
|
streamlit_data = convert_streamlit_to_v2_data(pd.streamlit) if pd.streamlit else {}
|
|
51
|
+
native_app_data = (
|
|
52
|
+
convert_native_app_to_v2_data(project_root, pd.native_app)
|
|
53
|
+
if pd.native_app
|
|
54
|
+
else {}
|
|
55
|
+
)
|
|
34
56
|
envs = convert_envs_to_v2(pd)
|
|
35
57
|
|
|
36
58
|
data = {
|
|
37
59
|
"definition_version": "2",
|
|
38
60
|
"entities": get_list_of_all_entities(
|
|
39
|
-
snowpark_data.get("entities", {}),
|
|
61
|
+
snowpark_data.get("entities", {}),
|
|
62
|
+
streamlit_data.get("entities", {}),
|
|
63
|
+
native_app_data.get("entities", {}),
|
|
40
64
|
),
|
|
41
65
|
"mixins": snowpark_data.get("mixins", None),
|
|
42
66
|
"env": envs,
|
|
@@ -99,7 +123,7 @@ def convert_snowpark_to_v2_data(snowpark: Snowpark) -> Dict[str, Any]:
|
|
|
99
123
|
return data
|
|
100
124
|
|
|
101
125
|
|
|
102
|
-
def convert_streamlit_to_v2_data(streamlit: Streamlit):
|
|
126
|
+
def convert_streamlit_to_v2_data(streamlit: Streamlit) -> Dict[str, Any]:
|
|
103
127
|
# Process env file and pages dir
|
|
104
128
|
environment_file = _process_streamlit_files(streamlit.env_file, "environment")
|
|
105
129
|
pages_dir = _process_streamlit_files(streamlit.pages_dir, "pages")
|
|
@@ -133,6 +157,7 @@ def convert_streamlit_to_v2_data(streamlit: Streamlit):
|
|
|
133
157
|
"type": "streamlit",
|
|
134
158
|
"identifier": identifier,
|
|
135
159
|
"title": streamlit.title,
|
|
160
|
+
"comment": streamlit.comment,
|
|
136
161
|
"query_warehouse": streamlit.query_warehouse,
|
|
137
162
|
"main_file": str(streamlit.main_file),
|
|
138
163
|
"pages_dir": str(streamlit.pages_dir),
|
|
@@ -144,6 +169,135 @@ def convert_streamlit_to_v2_data(streamlit: Streamlit):
|
|
|
144
169
|
return data
|
|
145
170
|
|
|
146
171
|
|
|
172
|
+
def convert_native_app_to_v2_data(
|
|
173
|
+
project_root, native_app: NativeApp
|
|
174
|
+
) -> Dict[str, Any]:
|
|
175
|
+
def _make_meta(obj: Application | Package):
|
|
176
|
+
meta = {}
|
|
177
|
+
if obj.role:
|
|
178
|
+
meta["role"] = obj.role
|
|
179
|
+
if obj.warehouse:
|
|
180
|
+
meta["warehouse"] = obj.warehouse
|
|
181
|
+
if obj.post_deploy:
|
|
182
|
+
meta["post_deploy"] = obj.post_deploy
|
|
183
|
+
return meta
|
|
184
|
+
|
|
185
|
+
def _find_manifest():
|
|
186
|
+
# We don't know which file in the project directory is the actual manifest,
|
|
187
|
+
# and we can't iterate through the artifacts property since the src can contain
|
|
188
|
+
# glob patterns. The simplest solution is to bundle the app and find the
|
|
189
|
+
# manifest file from the resultant BundleMap, since the bundle process ensures
|
|
190
|
+
# that only a single source path can map to the corresponding destination path
|
|
191
|
+
try:
|
|
192
|
+
bundle_map = build_bundle(
|
|
193
|
+
project_root, Path(native_app.deploy_root), native_app.artifacts
|
|
194
|
+
)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
# The manifest field is required, so we can't gracefully handle bundle failures
|
|
197
|
+
raise ClickException(
|
|
198
|
+
f"{e}\nCould not bundle Native App artifacts, unable to perform migration"
|
|
199
|
+
) from e
|
|
200
|
+
|
|
201
|
+
manifest_path = bundle_map.to_project_path(Path("manifest.yml"))
|
|
202
|
+
if not manifest_path:
|
|
203
|
+
# The manifest field is required, so we can't gracefully handle it being missing
|
|
204
|
+
raise ClickException(
|
|
205
|
+
"manifest.yml file not found in any Native App artifact sources, "
|
|
206
|
+
"unable to perform migration"
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Use a POSIX path to be consistent with other migrated fields
|
|
210
|
+
# which use POSIX paths as default values
|
|
211
|
+
return manifest_path.as_posix()
|
|
212
|
+
|
|
213
|
+
def _make_template(template: str) -> str:
|
|
214
|
+
return f"{PROJECT_TEMPLATE_VARIABLE_OPENING} {template} {PROJECT_TEMPLATE_VARIABLE_CLOSING}"
|
|
215
|
+
|
|
216
|
+
def _convert_package_script_files(package_scripts: list[str]):
|
|
217
|
+
# PDFv2 doesn't support package scripts, only post-deploy scripts, so we
|
|
218
|
+
# need to convert the Jinja syntax from {{ }} to <% %>
|
|
219
|
+
# Luckily, package scripts only support {{ package_name }}, so let's convert that tag
|
|
220
|
+
# to v2 template syntax by running it though the template process with a fake
|
|
221
|
+
# package name that's actually a valid v2 template, which will be evaluated
|
|
222
|
+
# when the script is used as a post-deploy script
|
|
223
|
+
fake_package_replacement_template = _make_template(
|
|
224
|
+
f"ctx.entities.{package_entity_name}.identifier"
|
|
225
|
+
)
|
|
226
|
+
jinja_context = dict(package_name=fake_package_replacement_template)
|
|
227
|
+
post_deploy_hooks = []
|
|
228
|
+
for script_file in package_scripts:
|
|
229
|
+
new_contents = render_script_template(
|
|
230
|
+
project_root, jinja_context, script_file, get_basic_jinja_env()
|
|
231
|
+
)
|
|
232
|
+
(project_root / script_file).write_text(new_contents)
|
|
233
|
+
post_deploy_hooks.append(SqlScriptHookType(sql_script=script_file))
|
|
234
|
+
return post_deploy_hooks
|
|
235
|
+
|
|
236
|
+
package_entity_name = "pkg"
|
|
237
|
+
if (
|
|
238
|
+
native_app.package
|
|
239
|
+
and native_app.package.name
|
|
240
|
+
and native_app.package.name != PackageV11.model_fields["name"].default
|
|
241
|
+
):
|
|
242
|
+
package_identifier = native_app.package.name
|
|
243
|
+
else:
|
|
244
|
+
# Backport the PackageV11 default name template, updated for PDFv2
|
|
245
|
+
package_identifier = _make_template(
|
|
246
|
+
f"fn.concat_ids('{native_app.name}', '_pkg_', fn.sanitize_id(fn.get_username('unknown_user')) | lower)"
|
|
247
|
+
)
|
|
248
|
+
package = {
|
|
249
|
+
"type": "application package",
|
|
250
|
+
"identifier": package_identifier,
|
|
251
|
+
"manifest": _find_manifest(),
|
|
252
|
+
"artifacts": native_app.artifacts,
|
|
253
|
+
"bundle_root": native_app.bundle_root,
|
|
254
|
+
"generated_root": native_app.generated_root,
|
|
255
|
+
"deploy_root": native_app.deploy_root,
|
|
256
|
+
"stage": native_app.source_stage,
|
|
257
|
+
"scratch_stage": native_app.scratch_stage,
|
|
258
|
+
}
|
|
259
|
+
if native_app.package:
|
|
260
|
+
package["distribution"] = native_app.package.distribution
|
|
261
|
+
package_meta = _make_meta(native_app.package)
|
|
262
|
+
if native_app.package.scripts:
|
|
263
|
+
converted_post_deploy_hooks = _convert_package_script_files(
|
|
264
|
+
native_app.package.scripts
|
|
265
|
+
)
|
|
266
|
+
package_meta["post_deploy"] = (
|
|
267
|
+
package_meta.get("post_deploy", []) + converted_post_deploy_hooks
|
|
268
|
+
)
|
|
269
|
+
if package_meta:
|
|
270
|
+
package["meta"] = package_meta
|
|
271
|
+
|
|
272
|
+
app_entity_name = "app"
|
|
273
|
+
if (
|
|
274
|
+
native_app.application
|
|
275
|
+
and native_app.application.name
|
|
276
|
+
and native_app.application.name != ApplicationV11.model_fields["name"].default
|
|
277
|
+
):
|
|
278
|
+
app_identifier = native_app.application.name
|
|
279
|
+
else:
|
|
280
|
+
# Backport the ApplicationV11 default name template, updated for PDFv2
|
|
281
|
+
app_identifier = _make_template(
|
|
282
|
+
f"fn.concat_ids('{native_app.name}', '_', fn.sanitize_id(fn.get_username('unknown_user')) | lower)"
|
|
283
|
+
)
|
|
284
|
+
app = {
|
|
285
|
+
"type": "application",
|
|
286
|
+
"identifier": app_identifier,
|
|
287
|
+
"from": {"target": package_entity_name},
|
|
288
|
+
}
|
|
289
|
+
if native_app.application:
|
|
290
|
+
if app_meta := _make_meta(native_app.application):
|
|
291
|
+
app["meta"] = app_meta
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
"entities": {
|
|
295
|
+
package_entity_name: package,
|
|
296
|
+
app_entity_name: app,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
|
|
147
301
|
def convert_envs_to_v2(pd: ProjectDefinition):
|
|
148
302
|
if hasattr(pd, "env") and pd.env:
|
|
149
303
|
data = {k: v for k, v in pd.env.items()}
|
|
@@ -166,10 +320,6 @@ def _check_if_project_definition_meets_requirements(
|
|
|
166
320
|
log.warning(
|
|
167
321
|
"Your V1 definition contains templates. We cannot guarantee the correctness of the migration."
|
|
168
322
|
)
|
|
169
|
-
if pd.native_app:
|
|
170
|
-
raise ClickException(
|
|
171
|
-
"Your project file contains a native app definition. Conversion of Native apps is not yet supported"
|
|
172
|
-
)
|
|
173
323
|
|
|
174
324
|
|
|
175
325
|
def _process_streamlit_files(
|
|
@@ -185,10 +335,19 @@ def _process_streamlit_files(
|
|
|
185
335
|
|
|
186
336
|
|
|
187
337
|
def get_list_of_all_entities(
|
|
188
|
-
snowpark_entities: Dict[str, Any],
|
|
338
|
+
snowpark_entities: Dict[str, Any],
|
|
339
|
+
streamlit_entities: Dict[str, Any],
|
|
340
|
+
native_app_entities: Dict[str, Any],
|
|
189
341
|
):
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
)
|
|
194
|
-
|
|
342
|
+
# Check all combinations of entity types for overlapping names
|
|
343
|
+
# (No need to use itertools here, PDFv1 only supports these three types)
|
|
344
|
+
for types, first, second in [
|
|
345
|
+
("streamlit and snowpark", streamlit_entities, snowpark_entities),
|
|
346
|
+
("streamlit and native app", streamlit_entities, native_app_entities),
|
|
347
|
+
("native app and snowpark", native_app_entities, snowpark_entities),
|
|
348
|
+
]:
|
|
349
|
+
if first.keys() & second.keys():
|
|
350
|
+
raise ClickException(
|
|
351
|
+
f"In your project, {types} entities share the same name. Please rename them and try again."
|
|
352
|
+
)
|
|
353
|
+
return snowpark_entities | streamlit_entities | native_app_entities
|
|
@@ -15,11 +15,10 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from abc import ABC
|
|
18
|
-
from typing import Generic, List, Optional, TypeVar, Union
|
|
18
|
+
from typing import Dict, Generic, List, Optional, TypeVar, Union
|
|
19
19
|
|
|
20
20
|
from pydantic import Field, PrivateAttr, field_validator
|
|
21
21
|
from snowflake.cli.api.identifiers import FQN
|
|
22
|
-
from snowflake.cli.api.project.schemas.identifier_model import Identifier
|
|
23
22
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
24
23
|
IdentifierField,
|
|
25
24
|
UpdatableModel,
|
|
@@ -61,16 +60,10 @@ class MetaField(UpdatableModel):
|
|
|
61
60
|
return mixins
|
|
62
61
|
|
|
63
62
|
|
|
64
|
-
class
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
default=None,
|
|
69
|
-
)
|
|
70
|
-
stage: Optional[str] = Field(
|
|
71
|
-
title="Stage.",
|
|
72
|
-
default=None,
|
|
73
|
-
)
|
|
63
|
+
class Identifier(UpdatableModel):
|
|
64
|
+
name: str = Field(title="Entity name")
|
|
65
|
+
schema_: Optional[str] = Field(title="Entity schema", alias="schema", default=None)
|
|
66
|
+
database: Optional[str] = Field(title="Entity database", default=None)
|
|
74
67
|
|
|
75
68
|
|
|
76
69
|
class EntityModelBase(ABC, UpdatableModel):
|
|
@@ -124,9 +117,17 @@ class TargetField(UpdatableModel, Generic[TargetType]):
|
|
|
124
117
|
return self.__pydantic_generic_metadata__["args"][0]
|
|
125
118
|
|
|
126
119
|
|
|
127
|
-
|
|
120
|
+
class ImportsBaseModel:
|
|
121
|
+
imports: Optional[List[str]] = Field(
|
|
122
|
+
title="Stage and path to previously uploaded files you want to import",
|
|
123
|
+
default=[],
|
|
124
|
+
)
|
|
128
125
|
|
|
129
|
-
|
|
126
|
+
def get_imports_sql(self) -> str | None:
|
|
127
|
+
if not self.imports:
|
|
128
|
+
return None
|
|
129
|
+
imports = ", ".join(f"'{i}'" for i in self.imports)
|
|
130
|
+
return f"IMPORTS = ({imports})"
|
|
130
131
|
|
|
131
132
|
|
|
132
133
|
class ExternalAccessBaseModel:
|
|
@@ -16,23 +16,26 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
from typing import Dict, List, Union, get_args
|
|
18
18
|
|
|
19
|
-
from snowflake.cli.
|
|
20
|
-
from snowflake.cli.
|
|
21
|
-
ApplicationPackageEntity,
|
|
22
|
-
)
|
|
23
|
-
from snowflake.cli.api.entities.snowpark_entity import FunctionEntity, ProcedureEntity
|
|
24
|
-
from snowflake.cli.api.entities.streamlit_entity import StreamlitEntity
|
|
25
|
-
from snowflake.cli.api.project.schemas.entities.application_entity_model import (
|
|
19
|
+
from snowflake.cli._plugins.nativeapp.application_entity import ApplicationEntity
|
|
20
|
+
from snowflake.cli._plugins.nativeapp.application_entity_model import (
|
|
26
21
|
ApplicationEntityModel,
|
|
27
22
|
)
|
|
28
|
-
from snowflake.cli.
|
|
23
|
+
from snowflake.cli._plugins.nativeapp.application_package_entity import (
|
|
24
|
+
ApplicationPackageEntity,
|
|
25
|
+
)
|
|
26
|
+
from snowflake.cli._plugins.nativeapp.application_package_entity_model import (
|
|
29
27
|
ApplicationPackageEntityModel,
|
|
30
28
|
)
|
|
31
|
-
from snowflake.cli.
|
|
29
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity import (
|
|
30
|
+
FunctionEntity,
|
|
31
|
+
ProcedureEntity,
|
|
32
|
+
)
|
|
33
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
|
|
32
34
|
FunctionEntityModel,
|
|
33
35
|
ProcedureEntityModel,
|
|
34
36
|
)
|
|
35
|
-
from snowflake.cli.
|
|
37
|
+
from snowflake.cli._plugins.streamlit.streamlit_entity import StreamlitEntity
|
|
38
|
+
from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
|
|
36
39
|
StreamlitEntityModel,
|
|
37
40
|
)
|
|
38
41
|
|