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.
Files changed (92) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +10 -1
  3. snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -0
  4. snowflake/cli/_app/secret.py +9 -0
  5. snowflake/cli/_app/snow_connector.py +110 -51
  6. snowflake/cli/_app/telemetry.py +8 -4
  7. snowflake/cli/_app/version_check.py +74 -0
  8. snowflake/cli/_plugins/git/commands.py +55 -14
  9. snowflake/cli/_plugins/git/manager.py +53 -7
  10. snowflake/cli/_plugins/helpers/commands.py +57 -0
  11. snowflake/cli/{api/commands/typer_pre_execute.py → _plugins/helpers/plugin_spec.py} +14 -10
  12. snowflake/cli/_plugins/nativeapp/application_entity.py +651 -0
  13. snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_entity_model.py +2 -2
  14. snowflake/cli/_plugins/nativeapp/application_package_entity.py +1107 -0
  15. snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_package_entity_model.py +3 -3
  16. snowflake/cli/_plugins/nativeapp/artifacts.py +10 -9
  17. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  18. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  19. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -1
  20. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
  21. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
  22. snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +1 -1
  23. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +3 -6
  24. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +50 -32
  25. snowflake/cli/_plugins/nativeapp/commands.py +84 -16
  26. snowflake/cli/_plugins/nativeapp/exceptions.py +0 -9
  27. snowflake/cli/_plugins/nativeapp/manager.py +56 -92
  28. snowflake/cli/_plugins/nativeapp/policy.py +3 -0
  29. snowflake/cli/_plugins/nativeapp/project_model.py +2 -2
  30. snowflake/cli/_plugins/nativeapp/run_processor.py +65 -272
  31. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +70 -0
  32. snowflake/cli/_plugins/nativeapp/teardown_processor.py +11 -154
  33. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +150 -40
  34. snowflake/cli/_plugins/nativeapp/version/commands.py +6 -24
  35. snowflake/cli/_plugins/nativeapp/version/version_processor.py +35 -235
  36. snowflake/cli/_plugins/snowpark/commands.py +5 -5
  37. snowflake/cli/_plugins/snowpark/common.py +4 -4
  38. snowflake/cli/_plugins/snowpark/models.py +2 -1
  39. snowflake/cli/{api/entities → _plugins/snowpark}/snowpark_entity.py +2 -2
  40. snowflake/cli/{api/project/schemas/entities/snowpark_entity.py → _plugins/snowpark/snowpark_entity_model.py} +3 -6
  41. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +1 -1
  42. snowflake/cli/_plugins/stage/manager.py +9 -4
  43. snowflake/cli/_plugins/streamlit/commands.py +4 -4
  44. snowflake/cli/_plugins/streamlit/manager.py +17 -4
  45. snowflake/cli/{api/entities → _plugins/streamlit}/streamlit_entity.py +2 -2
  46. snowflake/cli/{api/project/schemas/entities → _plugins/streamlit}/streamlit_entity_model.py +5 -12
  47. snowflake/cli/_plugins/workspace/action_context.py +2 -1
  48. snowflake/cli/_plugins/workspace/commands.py +127 -48
  49. snowflake/cli/_plugins/workspace/manager.py +1 -0
  50. snowflake/cli/_plugins/workspace/plugin_spec.py +1 -1
  51. snowflake/cli/api/cli_global_context.py +136 -313
  52. snowflake/cli/api/commands/flags.py +76 -91
  53. snowflake/cli/api/commands/snow_typer.py +7 -5
  54. snowflake/cli/api/config.py +1 -1
  55. snowflake/cli/api/connections.py +214 -0
  56. snowflake/cli/api/console/abc.py +4 -2
  57. snowflake/cli/api/entities/common.py +4 -0
  58. snowflake/cli/api/entities/utils.py +41 -31
  59. snowflake/cli/api/errno.py +1 -0
  60. snowflake/cli/api/identifiers.py +7 -3
  61. snowflake/cli/api/project/definition.py +11 -0
  62. snowflake/cli/api/project/definition_conversion.py +175 -16
  63. snowflake/cli/api/project/schemas/entities/common.py +15 -14
  64. snowflake/cli/api/project/schemas/entities/entities.py +13 -10
  65. snowflake/cli/api/project/schemas/project_definition.py +107 -45
  66. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  67. snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +0 -7
  68. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  69. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
  70. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  71. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
  72. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
  73. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  74. snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
  75. snowflake/cli/api/rendering/project_definition_templates.py +4 -0
  76. snowflake/cli/api/rendering/sql_templates.py +7 -0
  77. snowflake/cli/api/sql_execution.py +6 -15
  78. snowflake/cli/api/utils/definition_rendering.py +3 -1
  79. {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/METADATA +9 -9
  80. {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/RECORD +88 -81
  81. snowflake/cli/api/entities/application_entity.py +0 -12
  82. snowflake/cli/api/entities/application_package_entity.py +0 -553
  83. snowflake/cli/api/project/schemas/snowpark/__init__.py +0 -13
  84. snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
  85. /snowflake/cli/{api/project/schemas/native_app → _plugins/helpers}/__init__.py +0 -0
  86. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +0 -0
  87. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +0 -0
  88. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
  89. /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
  90. {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/WHEEL +0 -0
  91. {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/entry_points.txt +0 -0
  92. {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
- scripts_contents = []
307
- for relpath in scripts:
308
- script_full_path = SecurePath(project_root) / relpath
309
- try:
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
- except jinja2.TemplateSyntaxError as e:
321
- raise InvalidTemplateInFileError(relpath, e, e.lineno) from e
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
- except jinja2.UndefinedError as e:
324
- raise InvalidTemplateInFileError(relpath, e) from e
316
+ except jinja2.TemplateSyntaxError as e:
317
+ raise InvalidTemplateInFileError(script, e, e.lineno) from e
325
318
 
326
- return scripts_contents
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("")
@@ -17,6 +17,7 @@ NO_WAREHOUSE_SELECTED_IN_SESSION = 606
17
17
 
18
18
  DOES_NOT_EXIST_OR_NOT_AUTHORIZED = 2003
19
19
  DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED = 2043
20
+ INSUFFICIENT_PRIVILEGES = 3001
20
21
 
21
22
  # Native Apps
22
23
  CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION = 93044
@@ -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: Identifier) -> "FQN":
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.snowpark.callable import (
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", {}), streamlit_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], streamlit_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
- if snowpark_entities.keys() & streamlit_entities.keys():
191
- raise ClickException(
192
- "In your project, streamlit and snowpark entities share the same name. Please rename them and try again."
193
- )
194
- return snowpark_entities | streamlit_entities
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 DefaultsField(UpdatableModel):
65
- schema_: Optional[str] = Field(
66
- title="Schema.",
67
- alias="schema",
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
- from typing import Dict, List, Optional
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
- from pydantic import Field
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.api.entities.application_entity import ApplicationEntity
20
- from snowflake.cli.api.entities.application_package_entity import (
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.api.project.schemas.entities.application_package_entity_model import (
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.api.project.schemas.entities.snowpark_entity import (
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.api.project.schemas.entities.streamlit_entity_model import (
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