snowflake-cli 3.2.2__py3-none-any.whl → 3.4.1__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/__main__.py +2 -2
- snowflake/cli/_app/cli_app.py +224 -192
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +1 -27
- snowflake/cli/_app/constants.py +4 -0
- snowflake/cli/_app/snow_connector.py +12 -0
- snowflake/cli/_app/telemetry.py +10 -3
- snowflake/cli/_plugins/connection/util.py +12 -19
- snowflake/cli/_plugins/cortex/commands.py +2 -4
- snowflake/cli/_plugins/git/manager.py +1 -1
- snowflake/cli/_plugins/helpers/commands.py +207 -1
- snowflake/cli/_plugins/nativeapp/artifacts.py +16 -628
- 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 +42 -20
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +9 -2
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +6 -3
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +44 -34
- snowflake/cli/_plugins/nativeapp/commands.py +113 -21
- snowflake/cli/_plugins/nativeapp/constants.py +5 -0
- snowflake/cli/_plugins/nativeapp/entities/application.py +226 -296
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +911 -141
- snowflake/cli/_plugins/nativeapp/entities/application_package_child_interface.py +43 -0
- snowflake/cli/_plugins/nativeapp/feature_flags.py +5 -1
- snowflake/cli/_plugins/nativeapp/release_channel/__init__.py +13 -0
- snowflake/cli/_plugins/nativeapp/release_channel/commands.py +246 -0
- snowflake/cli/_plugins/nativeapp/release_directive/__init__.py +13 -0
- snowflake/cli/_plugins/nativeapp/release_directive/commands.py +243 -0
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +9 -17
- snowflake/cli/_plugins/nativeapp/sf_facade_exceptions.py +80 -0
- snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +1184 -80
- snowflake/cli/_plugins/nativeapp/utils.py +11 -0
- snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +7 -3
- snowflake/cli/_plugins/nativeapp/version/commands.py +32 -5
- snowflake/cli/_plugins/notebook/commands.py +55 -2
- snowflake/cli/_plugins/notebook/exceptions.py +1 -1
- snowflake/cli/_plugins/notebook/manager.py +7 -5
- snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
- snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
- snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
- snowflake/cli/_plugins/notebook/types.py +3 -0
- snowflake/cli/_plugins/snowpark/commands.py +48 -30
- snowflake/cli/_plugins/snowpark/common.py +47 -2
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +247 -4
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
- snowflake/cli/_plugins/snowpark/zipper.py +33 -1
- snowflake/cli/_plugins/spcs/common.py +129 -0
- snowflake/cli/_plugins/spcs/services/commands.py +131 -14
- snowflake/cli/_plugins/spcs/services/manager.py +169 -1
- snowflake/cli/_plugins/stage/commands.py +2 -1
- snowflake/cli/_plugins/stage/diff.py +60 -39
- snowflake/cli/_plugins/stage/manager.py +34 -13
- snowflake/cli/_plugins/stage/utils.py +1 -1
- snowflake/cli/_plugins/streamlit/commands.py +10 -1
- snowflake/cli/_plugins/streamlit/manager.py +70 -22
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +131 -1
- snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
- snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
- snowflake/cli/_plugins/workspace/commands.py +6 -5
- snowflake/cli/_plugins/workspace/manager.py +9 -5
- snowflake/cli/api/artifacts/__init__.py +13 -0
- snowflake/cli/api/artifacts/bundle_map.py +500 -0
- snowflake/cli/api/artifacts/common.py +78 -0
- snowflake/cli/api/artifacts/utils.py +82 -0
- snowflake/cli/api/cli_global_context.py +36 -2
- snowflake/cli/api/commands/flags.py +10 -4
- snowflake/cli/api/commands/utils.py +28 -2
- snowflake/cli/api/config.py +6 -2
- snowflake/cli/api/connections.py +12 -1
- snowflake/cli/api/constants.py +10 -1
- snowflake/cli/api/entities/common.py +81 -14
- snowflake/cli/api/entities/resolver.py +160 -0
- snowflake/cli/api/entities/utils.py +65 -23
- snowflake/cli/api/errno.py +63 -3
- snowflake/cli/api/feature_flags.py +19 -4
- snowflake/cli/api/metrics.py +21 -27
- snowflake/cli/api/project/definition_conversion.py +4 -4
- snowflake/cli/api/project/project_paths.py +28 -0
- snowflake/cli/api/project/schemas/entities/common.py +130 -1
- snowflake/cli/api/project/schemas/entities/entities.py +4 -0
- snowflake/cli/api/project/schemas/project_definition.py +54 -6
- snowflake/cli/api/project/schemas/updatable_model.py +2 -2
- snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
- snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +1 -1
- snowflake/cli/api/project/util.py +45 -0
- snowflake/cli/api/secure_path.py +6 -0
- snowflake/cli/api/sql_execution.py +5 -1
- snowflake/cli/api/stage_path.py +7 -2
- snowflake/cli/api/utils/graph.py +3 -0
- snowflake/cli/api/utils/path_utils.py +24 -0
- {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/METADATA +14 -15
- {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/RECORD +96 -82
- {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/WHEEL +1 -1
- snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
- {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -33,23 +33,12 @@ from snowflake.cli._plugins.nativeapp.codegen.snowpark.python_processor import (
|
|
|
33
33
|
from snowflake.cli._plugins.nativeapp.codegen.templates.templates_processor import (
|
|
34
34
|
TemplatesProcessor,
|
|
35
35
|
)
|
|
36
|
-
from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
|
|
37
36
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
38
37
|
from snowflake.cli.api.console import cli_console as cc
|
|
39
38
|
from snowflake.cli.api.metrics import CLICounterField
|
|
40
|
-
from snowflake.cli.api.project.schemas.
|
|
41
|
-
ProcessorMapping,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
SNOWPARK_PROCESSOR = "snowpark"
|
|
45
|
-
NA_SETUP_PROCESSOR = "native app setup"
|
|
46
|
-
TEMPLATES_PROCESSOR = "templates"
|
|
39
|
+
from snowflake.cli.api.project.schemas.entities.common import ProcessorMapping
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
SNOWPARK_PROCESSOR: SnowparkAnnotationProcessor,
|
|
50
|
-
NA_SETUP_PROCESSOR: NativeAppSetupProcessor,
|
|
51
|
-
TEMPLATES_PROCESSOR: TemplatesProcessor,
|
|
52
|
-
}
|
|
41
|
+
ProcessorClassType = type[ArtifactProcessor]
|
|
53
42
|
|
|
54
43
|
|
|
55
44
|
class NativeAppCompiler:
|
|
@@ -66,10 +55,28 @@ class NativeAppCompiler:
|
|
|
66
55
|
bundle_ctx: BundleContext,
|
|
67
56
|
):
|
|
68
57
|
self._assert_absolute_paths(bundle_ctx)
|
|
58
|
+
self._processor_classes_by_name: Dict[str, ProcessorClassType] = {}
|
|
69
59
|
self._bundle_ctx = bundle_ctx
|
|
70
60
|
# dictionary of all processors created and shared between different artifact objects.
|
|
71
61
|
self.cached_processors: Dict[str, ArtifactProcessor] = {}
|
|
72
62
|
|
|
63
|
+
self.register(SnowparkAnnotationProcessor)
|
|
64
|
+
self.register(NativeAppSetupProcessor)
|
|
65
|
+
self.register(TemplatesProcessor)
|
|
66
|
+
|
|
67
|
+
def register(self, processor_cls: ProcessorClassType):
|
|
68
|
+
"""
|
|
69
|
+
Registers a processor class to enable.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
name = getattr(processor_cls, "NAME", None)
|
|
73
|
+
assert name is not None
|
|
74
|
+
|
|
75
|
+
if name in self._processor_classes_by_name:
|
|
76
|
+
raise ValueError(f"Processor {name} is already registered")
|
|
77
|
+
|
|
78
|
+
self._processor_classes_by_name[str(name)] = processor_cls
|
|
79
|
+
|
|
73
80
|
@staticmethod
|
|
74
81
|
def _assert_absolute_paths(bundle_ctx: BundleContext):
|
|
75
82
|
for name in ["Project", "Deploy", "Bundle", "Generated"]:
|
|
@@ -88,7 +95,10 @@ class NativeAppCompiler:
|
|
|
88
95
|
if not self._should_invoke_processors():
|
|
89
96
|
return
|
|
90
97
|
|
|
91
|
-
with
|
|
98
|
+
with (
|
|
99
|
+
cc.phase("Invoking artifact processors"),
|
|
100
|
+
get_cli_context().metrics.span("artifact_processors"),
|
|
101
|
+
):
|
|
92
102
|
if self._bundle_ctx.generated_root.exists():
|
|
93
103
|
raise ClickException(
|
|
94
104
|
f"Path {self._bundle_ctx.generated_root} already exists. Please choose a different name for your generated directory in the project definition file."
|
|
@@ -125,8 +135,8 @@ class NativeAppCompiler:
|
|
|
125
135
|
if current_processor is not None:
|
|
126
136
|
return current_processor
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
if
|
|
138
|
+
processor_cls = self._processor_classes_by_name.get(processor_name)
|
|
139
|
+
if processor_cls is None:
|
|
130
140
|
# No registered processor with the specified name
|
|
131
141
|
return None
|
|
132
142
|
|
|
@@ -138,7 +148,7 @@ class NativeAppCompiler:
|
|
|
138
148
|
processor_ctx.generated_root = (
|
|
139
149
|
self._bundle_ctx.generated_root / processor_subdirectory
|
|
140
150
|
)
|
|
141
|
-
current_processor =
|
|
151
|
+
current_processor = processor_cls(processor_ctx)
|
|
142
152
|
self.cached_processors[processor_name] = current_processor
|
|
143
153
|
|
|
144
154
|
return current_processor
|
|
@@ -151,6 +161,18 @@ class NativeAppCompiler:
|
|
|
151
161
|
return False
|
|
152
162
|
|
|
153
163
|
def _is_enabled(self, processor: ProcessorMapping) -> bool:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
"""
|
|
165
|
+
Determines is a process is enabled. All processors are considered enabled
|
|
166
|
+
unless they are explicitly disabled, typically via a feature flag.
|
|
167
|
+
"""
|
|
168
|
+
processor_name = processor.name.lower()
|
|
169
|
+
processor_cls = self._processor_classes_by_name.get(processor_name)
|
|
170
|
+
if processor_cls is None:
|
|
171
|
+
# Unknown processor, consider it enabled, even though trying to
|
|
172
|
+
# invoke it later will raise an exception
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
# if the processor class defines a static method named "is_enabled", then
|
|
176
|
+
# call it. Otherwise, it's considered enabled by default.
|
|
177
|
+
is_enabled_fn = getattr(processor_cls, "is_enabled", lambda: True)
|
|
178
|
+
return is_enabled_fn()
|
|
@@ -23,7 +23,6 @@ from typing import List, Optional
|
|
|
23
23
|
import yaml
|
|
24
24
|
from click import ClickException
|
|
25
25
|
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
26
|
-
BundleMap,
|
|
27
26
|
find_manifest_file,
|
|
28
27
|
find_setup_script_file,
|
|
29
28
|
)
|
|
@@ -36,9 +35,11 @@ from snowflake.cli._plugins.nativeapp.codegen.sandbox import (
|
|
|
36
35
|
SandboxEnvBuilder,
|
|
37
36
|
execute_script_in_sandbox,
|
|
38
37
|
)
|
|
38
|
+
from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
|
|
39
39
|
from snowflake.cli._plugins.stage.diff import to_stage_path
|
|
40
|
+
from snowflake.cli.api.artifacts.bundle_map import BundleMap
|
|
40
41
|
from snowflake.cli.api.console import cli_console as cc
|
|
41
|
-
from snowflake.cli.api.project.schemas.
|
|
42
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
42
43
|
PathMapping,
|
|
43
44
|
ProcessorMapping,
|
|
44
45
|
)
|
|
@@ -74,9 +75,15 @@ def safe_set(d: dict, *keys: str, **kwargs) -> None:
|
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
class NativeAppSetupProcessor(ArtifactProcessor):
|
|
78
|
+
NAME = "native app setup"
|
|
79
|
+
|
|
77
80
|
def __init__(self, *args, **kwargs):
|
|
78
81
|
super().__init__(*args, **kwargs)
|
|
79
82
|
|
|
83
|
+
@staticmethod
|
|
84
|
+
def is_enabled() -> bool:
|
|
85
|
+
return FeatureFlag.ENABLE_NATIVE_APP_PYTHON_SETUP.is_enabled()
|
|
86
|
+
|
|
80
87
|
def process(
|
|
81
88
|
self,
|
|
82
89
|
artifact_to_process: PathMapping,
|
|
@@ -22,7 +22,6 @@ from typing import Any, Dict, List, Optional, Set
|
|
|
22
22
|
|
|
23
23
|
from pydantic import ValidationError
|
|
24
24
|
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
25
|
-
BundleMap,
|
|
26
25
|
find_setup_script_file,
|
|
27
26
|
)
|
|
28
27
|
from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
|
|
@@ -48,10 +47,11 @@ from snowflake.cli._plugins.nativeapp.codegen.snowpark.models import (
|
|
|
48
47
|
NativeAppExtensionFunction,
|
|
49
48
|
)
|
|
50
49
|
from snowflake.cli._plugins.stage.diff import to_stage_path
|
|
51
|
-
from snowflake.cli.api.
|
|
50
|
+
from snowflake.cli.api.artifacts.bundle_map import BundleMap
|
|
51
|
+
from snowflake.cli.api.cli_global_context import get_cli_context, span
|
|
52
52
|
from snowflake.cli.api.console import cli_console as cc
|
|
53
53
|
from snowflake.cli.api.metrics import CLICounterField
|
|
54
|
-
from snowflake.cli.api.project.schemas.
|
|
54
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
55
55
|
PathMapping,
|
|
56
56
|
ProcessorMapping,
|
|
57
57
|
)
|
|
@@ -164,9 +164,12 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
|
|
|
164
164
|
and generate SQL code for creation of extension functions based on those discovered objects.
|
|
165
165
|
"""
|
|
166
166
|
|
|
167
|
+
NAME = "snowpark"
|
|
168
|
+
|
|
167
169
|
def __init__(self, *args, **kwargs):
|
|
168
170
|
super().__init__(*args, **kwargs)
|
|
169
171
|
|
|
172
|
+
@span("snowpark_processor")
|
|
170
173
|
def process(
|
|
171
174
|
self,
|
|
172
175
|
artifact_to_process: PathMapping,
|
|
@@ -18,15 +18,15 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any, Optional
|
|
19
19
|
|
|
20
20
|
import jinja2
|
|
21
|
-
from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
|
|
22
21
|
from snowflake.cli._plugins.nativeapp.codegen.artifact_processor import (
|
|
23
22
|
ArtifactProcessor,
|
|
24
23
|
)
|
|
25
24
|
from snowflake.cli._plugins.nativeapp.exceptions import InvalidTemplateInFileError
|
|
26
|
-
from snowflake.cli.api.
|
|
25
|
+
from snowflake.cli.api.artifacts.bundle_map import BundleMap
|
|
26
|
+
from snowflake.cli.api.cli_global_context import get_cli_context, span
|
|
27
27
|
from snowflake.cli.api.console import cli_console as cc
|
|
28
28
|
from snowflake.cli.api.metrics import CLICounterField
|
|
29
|
-
from snowflake.cli.api.project.schemas.
|
|
29
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
30
30
|
PathMapping,
|
|
31
31
|
ProcessorMapping,
|
|
32
32
|
)
|
|
@@ -49,6 +49,8 @@ class TemplatesProcessor(ArtifactProcessor):
|
|
|
49
49
|
Processor class to perform template expansion on all relevant artifacts (specified in the project definition file).
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
+
NAME = "templates"
|
|
53
|
+
|
|
52
54
|
def expand_templates_in_file(
|
|
53
55
|
self, src: Path, dest: Path, template_context: dict[str, Any] | None = None
|
|
54
56
|
) -> None:
|
|
@@ -58,39 +60,47 @@ class TemplatesProcessor(ArtifactProcessor):
|
|
|
58
60
|
if src.is_dir():
|
|
59
61
|
return
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
src_file_name = src.relative_to(self._bundle_ctx.project_root)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
with self.edit_file(dest) as file:
|
|
67
|
+
if not has_client_side_templates(file.contents) and not (
|
|
68
|
+
_is_sql_file(dest) and has_sql_templates(file.contents)
|
|
69
|
+
):
|
|
70
|
+
return
|
|
71
|
+
cc.step(f"Expanding templates in {src_file_name}")
|
|
72
|
+
with cc.indented():
|
|
73
|
+
try:
|
|
74
|
+
jinja_env = (
|
|
75
|
+
choose_sql_jinja_env_based_on_template_syntax(
|
|
76
|
+
file.contents, reference_name=src_file_name
|
|
77
|
+
)
|
|
78
|
+
if _is_sql_file(dest)
|
|
79
|
+
else get_client_side_jinja_env()
|
|
80
|
+
)
|
|
81
|
+
expanded_template = jinja_env.from_string(file.contents).render(
|
|
82
|
+
template_context or get_cli_context().template_context
|
|
74
83
|
)
|
|
75
|
-
if _is_sql_file(dest)
|
|
76
|
-
else get_client_side_jinja_env()
|
|
77
|
-
)
|
|
78
|
-
expanded_template = jinja_env.from_string(file.contents).render(
|
|
79
|
-
template_context or get_cli_context().template_context
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
# For now, we are printing the source file path in the error message
|
|
83
|
-
# instead of the destination file path to make it easier for the user
|
|
84
|
-
# to identify the file that has the error, and edit the correct file.
|
|
85
|
-
except jinja2.TemplateSyntaxError as e:
|
|
86
|
-
raise InvalidTemplateInFileError(src_file_name, e, e.lineno) from e
|
|
87
|
-
|
|
88
|
-
except jinja2.UndefinedError as e:
|
|
89
|
-
raise InvalidTemplateInFileError(src_file_name, e) from e
|
|
90
|
-
|
|
91
|
-
if expanded_template != file.contents:
|
|
92
|
-
file.edited_contents = expanded_template
|
|
93
84
|
|
|
85
|
+
# For now, we are printing the source file path in the error message
|
|
86
|
+
# instead of the destination file path to make it easier for the user
|
|
87
|
+
# to identify the file that has the error, and edit the correct file.
|
|
88
|
+
except jinja2.TemplateSyntaxError as e:
|
|
89
|
+
raise InvalidTemplateInFileError(
|
|
90
|
+
src_file_name, e, e.lineno
|
|
91
|
+
) from e
|
|
92
|
+
|
|
93
|
+
except jinja2.UndefinedError as e:
|
|
94
|
+
raise InvalidTemplateInFileError(src_file_name, e) from e
|
|
95
|
+
|
|
96
|
+
if expanded_template != file.contents:
|
|
97
|
+
file.edited_contents = expanded_template
|
|
98
|
+
except UnicodeDecodeError as err:
|
|
99
|
+
cc.warning(
|
|
100
|
+
f"Could not read file {src_file_name}, error: {err.reason}. Skipping this file."
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@span("templates_processor")
|
|
94
104
|
def process(
|
|
95
105
|
self,
|
|
96
106
|
artifact_to_process: PathMapping,
|
|
@@ -22,6 +22,7 @@ from textwrap import dedent
|
|
|
22
22
|
from typing import Generator, Iterable, List, Optional, cast
|
|
23
23
|
|
|
24
24
|
import typer
|
|
25
|
+
from snowflake.cli._plugins.nativeapp.artifacts import VersionInfo
|
|
25
26
|
from snowflake.cli._plugins.nativeapp.common_flags import (
|
|
26
27
|
ForceOption,
|
|
27
28
|
InteractiveOption,
|
|
@@ -31,23 +32,25 @@ from snowflake.cli._plugins.nativeapp.entities.application import ApplicationEnt
|
|
|
31
32
|
from snowflake.cli._plugins.nativeapp.entities.application_package import (
|
|
32
33
|
ApplicationPackageEntityModel,
|
|
33
34
|
)
|
|
35
|
+
from snowflake.cli._plugins.nativeapp.release_channel.commands import (
|
|
36
|
+
app as release_channels_app,
|
|
37
|
+
)
|
|
38
|
+
from snowflake.cli._plugins.nativeapp.release_directive.commands import (
|
|
39
|
+
app as release_directives_app,
|
|
40
|
+
)
|
|
41
|
+
from snowflake.cli._plugins.nativeapp.sf_facade import get_snowflake_facade
|
|
34
42
|
from snowflake.cli._plugins.nativeapp.v2_conversions.compat import (
|
|
35
43
|
find_entity,
|
|
36
44
|
force_project_definition_v2,
|
|
37
45
|
)
|
|
38
46
|
from snowflake.cli._plugins.nativeapp.version.commands import app as versions_app
|
|
39
|
-
from snowflake.cli._plugins.stage.diff import (
|
|
40
|
-
DiffResult,
|
|
41
|
-
compute_stage_diff,
|
|
42
|
-
)
|
|
43
|
-
from snowflake.cli._plugins.stage.utils import print_diff_to_console
|
|
44
47
|
from snowflake.cli._plugins.workspace.manager import WorkspaceManager
|
|
45
48
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
46
49
|
from snowflake.cli.api.commands.decorators import (
|
|
47
50
|
with_project_definition,
|
|
48
51
|
)
|
|
49
52
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
50
|
-
from snowflake.cli.api.entities.
|
|
53
|
+
from snowflake.cli.api.entities.utils import EntityActions
|
|
51
54
|
from snowflake.cli.api.exceptions import (
|
|
52
55
|
IncompatibleParametersError,
|
|
53
56
|
UnmetParametersError,
|
|
@@ -59,6 +62,7 @@ from snowflake.cli.api.output.types import (
|
|
|
59
62
|
ObjectResult,
|
|
60
63
|
StreamResult,
|
|
61
64
|
)
|
|
65
|
+
from snowflake.cli.api.project.util import same_identifiers
|
|
62
66
|
from typing_extensions import Annotated
|
|
63
67
|
|
|
64
68
|
app = SnowTyperFactory(
|
|
@@ -66,6 +70,8 @@ app = SnowTyperFactory(
|
|
|
66
70
|
help="Manages a Snowflake Native App",
|
|
67
71
|
)
|
|
68
72
|
app.add_typer(versions_app)
|
|
73
|
+
app.add_typer(release_directives_app)
|
|
74
|
+
app.add_typer(release_channels_app)
|
|
69
75
|
|
|
70
76
|
log = logging.getLogger(__name__)
|
|
71
77
|
|
|
@@ -108,20 +114,15 @@ def app_diff(
|
|
|
108
114
|
project_root=cli_context.project_root,
|
|
109
115
|
)
|
|
110
116
|
package_id = options["package_entity_id"]
|
|
111
|
-
|
|
112
|
-
bundle_map = ws.perform_action(
|
|
117
|
+
diff = ws.perform_action(
|
|
113
118
|
package_id,
|
|
114
|
-
EntityActions.
|
|
115
|
-
|
|
116
|
-
stage_fqn = f"{package.fqn.name}.{package.stage}"
|
|
117
|
-
diff: DiffResult = compute_stage_diff(
|
|
118
|
-
local_root=Path(package.deploy_root), stage_fqn=stage_fqn
|
|
119
|
+
EntityActions.DIFF,
|
|
120
|
+
print_to_console=cli_context.output_format != OutputFormat.JSON,
|
|
119
121
|
)
|
|
120
122
|
if cli_context.output_format == OutputFormat.JSON:
|
|
121
123
|
return ObjectResult(diff.to_dict())
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return None # don't print any output
|
|
124
|
+
|
|
125
|
+
return None
|
|
125
126
|
|
|
126
127
|
|
|
127
128
|
@app.command("run", requires_connection=True)
|
|
@@ -146,6 +147,12 @@ def app_run(
|
|
|
146
147
|
The command fails if no release directive exists for your Snowflake account for a given application package, which is determined from the project definition file. Default: unset.""",
|
|
147
148
|
is_flag=True,
|
|
148
149
|
),
|
|
150
|
+
channel: str = typer.Option(
|
|
151
|
+
None,
|
|
152
|
+
show_default=False,
|
|
153
|
+
help=f"""The name of the release channel to use when creating or upgrading an application instance from a release directive.
|
|
154
|
+
Requires the `--from-release-directive` flag to be set. If unset, the default channel will be used.""",
|
|
155
|
+
),
|
|
149
156
|
interactive: bool = InteractiveOption,
|
|
150
157
|
force: Optional[bool] = ForceOption,
|
|
151
158
|
validate: bool = ValidateOption,
|
|
@@ -174,6 +181,7 @@ def app_run(
|
|
|
174
181
|
paths=[],
|
|
175
182
|
interactive=interactive,
|
|
176
183
|
force=force,
|
|
184
|
+
release_channel=channel,
|
|
177
185
|
)
|
|
178
186
|
app = ws.get_entity(app_id)
|
|
179
187
|
return MessageResult(
|
|
@@ -198,7 +206,7 @@ def app_open(
|
|
|
198
206
|
)
|
|
199
207
|
app_id = options["app_entity_id"]
|
|
200
208
|
app = ws.get_entity(app_id)
|
|
201
|
-
if
|
|
209
|
+
if get_snowflake_facade().get_existing_app_info(app.name, app.role):
|
|
202
210
|
typer.launch(app.get_snowsight_url())
|
|
203
211
|
return MessageResult(f"Snowflake Native App opened in browser.")
|
|
204
212
|
else:
|
|
@@ -221,7 +229,7 @@ def app_teardown(
|
|
|
221
229
|
# Same as the param auto-added by @force_project_definition_v2 if single_app_and_package were true
|
|
222
230
|
package_entity_id: Optional[str] = typer.Option(
|
|
223
231
|
default="",
|
|
224
|
-
help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
|
|
232
|
+
help="The ID of the package entity on which to operate when the definition_version is 2 or higher.",
|
|
225
233
|
),
|
|
226
234
|
**options,
|
|
227
235
|
) -> CommandResult:
|
|
@@ -246,11 +254,22 @@ def app_teardown(
|
|
|
246
254
|
project_definition=cli_context.project_definition,
|
|
247
255
|
project_root=cli_context.project_root,
|
|
248
256
|
)
|
|
257
|
+
|
|
258
|
+
# TODO: get all apps created from this application package from snowflake, compare, confirm and drop.
|
|
259
|
+
# TODO: add messaging/confirmation here for extra apps found as part of above
|
|
260
|
+
all_packages_with_id = [
|
|
261
|
+
package_entity.entity_id
|
|
262
|
+
for package_entity in project.get_entities_by_type(
|
|
263
|
+
ApplicationPackageEntityModel.get_type()
|
|
264
|
+
).values()
|
|
265
|
+
if same_identifiers(package_entity.fqn.name, app_package_entity.fqn.name)
|
|
266
|
+
]
|
|
267
|
+
|
|
249
268
|
for app_entity in project.get_entities_by_type(
|
|
250
269
|
ApplicationEntityModel.get_type()
|
|
251
270
|
).values():
|
|
252
271
|
# Drop each app
|
|
253
|
-
if app_entity.from_.target
|
|
272
|
+
if app_entity.from_.target in all_packages_with_id:
|
|
254
273
|
ws.perform_action(
|
|
255
274
|
app_entity.entity_id,
|
|
256
275
|
EntityActions.DROP,
|
|
@@ -289,7 +308,7 @@ def app_deploy(
|
|
|
289
308
|
show_default=False,
|
|
290
309
|
help=dedent(
|
|
291
310
|
f"""
|
|
292
|
-
Paths, relative to the
|
|
311
|
+
Paths, relative to the project root, of files or directories you want to upload to a stage. If a file is
|
|
293
312
|
specified, it must match one of the artifacts src pattern entries in snowflake.yml. If a directory is
|
|
294
313
|
specified, it will be searched for subfolders or files to deploy based on artifacts src pattern entries. If
|
|
295
314
|
unspecified, the command syncs all local changes to the stage."""
|
|
@@ -357,7 +376,10 @@ def app_validate(
|
|
|
357
376
|
if cli_context.output_format == OutputFormat.JSON:
|
|
358
377
|
return ObjectResult(
|
|
359
378
|
package.get_validation_result(
|
|
360
|
-
|
|
379
|
+
action_ctx=ws.action_ctx,
|
|
380
|
+
use_scratch_stage=True,
|
|
381
|
+
interactive=False,
|
|
382
|
+
force=True,
|
|
361
383
|
)
|
|
362
384
|
)
|
|
363
385
|
|
|
@@ -513,3 +535,73 @@ class EventResult(ObjectResult, MessageResult):
|
|
|
513
535
|
@property
|
|
514
536
|
def result(self):
|
|
515
537
|
return self._element
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
@app.command("publish", requires_connection=True)
|
|
541
|
+
@with_project_definition()
|
|
542
|
+
@force_project_definition_v2()
|
|
543
|
+
def app_publish(
|
|
544
|
+
version: Optional[str] = typer.Option(
|
|
545
|
+
default=None,
|
|
546
|
+
show_default=False,
|
|
547
|
+
help="The version to publish to the provided release channel and release directive. Version is required to exist unless `--create-version` flag is used.",
|
|
548
|
+
),
|
|
549
|
+
patch: Optional[int] = typer.Option(
|
|
550
|
+
default=None,
|
|
551
|
+
show_default=False,
|
|
552
|
+
help="The patch number under the given version. This will be used when setting the release directive. Patch is required to exist unless `--create-version` flag is used.",
|
|
553
|
+
),
|
|
554
|
+
channel: Optional[str] = typer.Option(
|
|
555
|
+
"DEFAULT",
|
|
556
|
+
help="The name of the release channel to publish to. If not provided, the default release channel is used.",
|
|
557
|
+
),
|
|
558
|
+
directive: Optional[str] = typer.Option(
|
|
559
|
+
"DEFAULT",
|
|
560
|
+
help="The name of the release directive to update with the specified version and patch. If not provided, the default release directive is used.",
|
|
561
|
+
),
|
|
562
|
+
interactive: bool = InteractiveOption,
|
|
563
|
+
force: Optional[bool] = ForceOption,
|
|
564
|
+
create_version: bool = typer.Option(
|
|
565
|
+
False,
|
|
566
|
+
"--create-version",
|
|
567
|
+
help="Create a new version or patch based on the provided `--version` and `--patch` values. Fallback to the manifest values if not provided.",
|
|
568
|
+
is_flag=True,
|
|
569
|
+
),
|
|
570
|
+
from_stage: bool = typer.Option(
|
|
571
|
+
False,
|
|
572
|
+
"--from-stage",
|
|
573
|
+
help="When enabled, the Snowflake CLI creates a version from the current application package stage without syncing to the stage first. Can only be used with `--create-version` flag.",
|
|
574
|
+
is_flag=True,
|
|
575
|
+
),
|
|
576
|
+
label: Optional[str] = typer.Option(
|
|
577
|
+
None,
|
|
578
|
+
"--label",
|
|
579
|
+
help="A label for the version that is displayed to consumers. Can only be used with `--create-version` flag.",
|
|
580
|
+
),
|
|
581
|
+
**options,
|
|
582
|
+
) -> CommandResult:
|
|
583
|
+
"""
|
|
584
|
+
Adds the version to the release channel and updates the release directive with the new version and patch.
|
|
585
|
+
"""
|
|
586
|
+
cli_context = get_cli_context()
|
|
587
|
+
ws = WorkspaceManager(
|
|
588
|
+
project_definition=cli_context.project_definition,
|
|
589
|
+
project_root=cli_context.project_root,
|
|
590
|
+
)
|
|
591
|
+
package_id = options["package_entity_id"]
|
|
592
|
+
version_info: VersionInfo = ws.perform_action(
|
|
593
|
+
package_id,
|
|
594
|
+
EntityActions.PUBLISH,
|
|
595
|
+
version=version,
|
|
596
|
+
patch=patch,
|
|
597
|
+
release_channel=channel,
|
|
598
|
+
release_directive=directive,
|
|
599
|
+
interactive=interactive,
|
|
600
|
+
force=force,
|
|
601
|
+
create_version=create_version,
|
|
602
|
+
from_stage=from_stage,
|
|
603
|
+
label=label,
|
|
604
|
+
)
|
|
605
|
+
return MessageResult(
|
|
606
|
+
f"Version {version_info.version_name} and patch {version_info.patch_number} published to release directive {directive} of release channel {channel}."
|
|
607
|
+
)
|
|
@@ -22,7 +22,12 @@ COMMENT_COL = "comment"
|
|
|
22
22
|
OWNER_COL = "owner"
|
|
23
23
|
VERSION_COL = "version"
|
|
24
24
|
PATCH_COL = "patch"
|
|
25
|
+
CHANNEL_COL = "release_channel_name"
|
|
25
26
|
AUTHORIZE_TELEMETRY_COL = "authorize_telemetry_event_sharing"
|
|
26
27
|
|
|
27
28
|
INTERNAL_DISTRIBUTION = "internal"
|
|
28
29
|
EXTERNAL_DISTRIBUTION = "external"
|
|
30
|
+
|
|
31
|
+
DEFAULT_CHANNEL = "DEFAULT"
|
|
32
|
+
DEFAULT_DIRECTIVE = "DEFAULT"
|
|
33
|
+
MAX_VERSIONS_IN_RELEASE_CHANNEL = 2
|