snowflake-cli 3.4.1__py3-none-any.whl → 3.5.0__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 +1 -10
- snowflake/cli/_app/commands_registration/builtin_plugins.py +5 -1
- snowflake/cli/_app/commands_registration/command_plugins_loader.py +3 -1
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +3 -3
- snowflake/cli/_app/printing.py +2 -2
- snowflake/cli/_plugins/connection/commands.py +2 -4
- snowflake/cli/_plugins/helpers/commands.py +3 -4
- snowflake/cli/_plugins/notebook/commands.py +3 -4
- snowflake/cli/_plugins/plugin/commands.py +79 -0
- snowflake/cli/_plugins/plugin/manager.py +74 -0
- snowflake/cli/_plugins/plugin/plugin_spec.py +30 -0
- snowflake/cli/_plugins/project/__init__.py +0 -0
- snowflake/cli/_plugins/project/commands.py +157 -0
- snowflake/cli/{_app/api_impl/plugin/__init__.py → _plugins/project/feature_flags.py} +9 -0
- snowflake/cli/_plugins/project/manager.py +76 -0
- snowflake/cli/_plugins/project/plugin_spec.py +30 -0
- snowflake/cli/_plugins/project/project_entity_model.py +40 -0
- snowflake/cli/_plugins/snowpark/commands.py +2 -1
- snowflake/cli/_plugins/spcs/compute_pool/commands.py +53 -5
- snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity.py +8 -0
- snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity_model.py +37 -0
- snowflake/cli/_plugins/spcs/compute_pool/manager.py +45 -0
- snowflake/cli/_plugins/spcs/image_repository/commands.py +29 -0
- snowflake/cli/_plugins/spcs/image_repository/image_repository_entity.py +8 -0
- snowflake/cli/_plugins/spcs/image_repository/image_repository_entity_model.py +8 -0
- snowflake/cli/_plugins/spcs/image_repository/manager.py +1 -1
- snowflake/cli/_plugins/spcs/services/commands.py +53 -0
- snowflake/cli/_plugins/spcs/services/manager.py +114 -0
- snowflake/cli/_plugins/spcs/services/service_entity.py +6 -0
- snowflake/cli/_plugins/spcs/services/service_entity_model.py +45 -0
- snowflake/cli/_plugins/spcs/services/service_project_paths.py +15 -0
- snowflake/cli/_plugins/stage/manager.py +2 -2
- snowflake/cli/_plugins/streamlit/commands.py +9 -24
- snowflake/cli/_plugins/streamlit/manager.py +5 -36
- snowflake/cli/api/artifacts/upload.py +51 -0
- snowflake/cli/api/commands/flags.py +24 -9
- snowflake/cli/api/commands/snow_typer.py +12 -0
- snowflake/cli/api/commands/utils.py +2 -0
- snowflake/cli/api/config.py +15 -10
- snowflake/cli/api/exceptions.py +8 -1
- snowflake/cli/api/feature_flags.py +1 -0
- snowflake/cli/api/plugins/plugin_config.py +43 -4
- snowflake/cli/api/project/definition_helper.py +31 -0
- snowflake/cli/api/project/schemas/entities/entities.py +26 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/METADATA +9 -9
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/RECORD +51 -36
- snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
- snowflake/cli/api/__init__.py +0 -48
- /snowflake/cli/{_app/api_impl → _plugins/plugin}/__init__.py +0 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/licenses/LICENSE +0 -0
snowflake/cli/__about__.py
CHANGED
snowflake/cli/_app/cli_app.py
CHANGED
|
@@ -25,9 +25,6 @@ import click
|
|
|
25
25
|
import typer
|
|
26
26
|
from click import Context as ClickContext
|
|
27
27
|
from snowflake.cli import __about__
|
|
28
|
-
from snowflake.cli._app.api_impl.plugin.plugin_config_provider_impl import (
|
|
29
|
-
PluginConfigProviderImpl,
|
|
30
|
-
)
|
|
31
28
|
from snowflake.cli._app.commands_registration.commands_registration_with_callbacks import (
|
|
32
29
|
CommandsRegistrationWithCallbacks,
|
|
33
30
|
)
|
|
@@ -42,7 +39,6 @@ from snowflake.cli._app.version_check import (
|
|
|
42
39
|
get_new_version_msg,
|
|
43
40
|
show_new_version_banner_callback,
|
|
44
41
|
)
|
|
45
|
-
from snowflake.cli.api import Api, api_provider
|
|
46
42
|
from snowflake.cli.api.config import config_init, get_feature_flags_section
|
|
47
43
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
48
44
|
from snowflake.cli.api.output.types import CollectionResult
|
|
@@ -63,12 +59,7 @@ def _do_not_execute_on_completion(callback):
|
|
|
63
59
|
|
|
64
60
|
class CliAppFactory:
|
|
65
61
|
def __init__(self):
|
|
66
|
-
|
|
67
|
-
self._api = api
|
|
68
|
-
self._commands_registration = CommandsRegistrationWithCallbacks(
|
|
69
|
-
api.plugin_config_provider
|
|
70
|
-
)
|
|
71
|
-
api_provider.register_api(api)
|
|
62
|
+
self._commands_registration = CommandsRegistrationWithCallbacks()
|
|
72
63
|
self._app: Optional[SnowCliMainTyper] = None
|
|
73
64
|
self._click_context: Optional[ClickContext] = None
|
|
74
65
|
|
|
@@ -20,6 +20,8 @@ from snowflake.cli._plugins.init import plugin_spec as init_plugin_spec
|
|
|
20
20
|
from snowflake.cli._plugins.nativeapp import plugin_spec as nativeapp_plugin_spec
|
|
21
21
|
from snowflake.cli._plugins.notebook import plugin_spec as notebook_plugin_spec
|
|
22
22
|
from snowflake.cli._plugins.object import plugin_spec as object_plugin_spec
|
|
23
|
+
from snowflake.cli._plugins.plugin import plugin_spec as plugin_plugin_spec
|
|
24
|
+
from snowflake.cli._plugins.project import plugin_spec as project_plugin_spec
|
|
23
25
|
from snowflake.cli._plugins.snowpark import plugin_spec as snowpark_plugin_spec
|
|
24
26
|
from snowflake.cli._plugins.spcs import plugin_spec as spcs_plugin_spec
|
|
25
27
|
from snowflake.cli._plugins.sql import plugin_spec as sql_plugin_spec
|
|
@@ -34,8 +36,9 @@ def get_builtin_plugin_name_to_plugin_spec():
|
|
|
34
36
|
"connection": connection_plugin_spec,
|
|
35
37
|
"helpers": migrate_plugin_spec,
|
|
36
38
|
"spcs": spcs_plugin_spec,
|
|
37
|
-
"
|
|
39
|
+
"app": nativeapp_plugin_spec,
|
|
38
40
|
"object": object_plugin_spec,
|
|
41
|
+
"project": project_plugin_spec,
|
|
39
42
|
"snowpark": snowpark_plugin_spec,
|
|
40
43
|
"stage": stage_plugin_spec,
|
|
41
44
|
"sql": sql_plugin_spec,
|
|
@@ -45,6 +48,7 @@ def get_builtin_plugin_name_to_plugin_spec():
|
|
|
45
48
|
"cortex": cortex_plugin_spec,
|
|
46
49
|
"init": init_plugin_spec,
|
|
47
50
|
"workspace": workspace_plugin_spec,
|
|
51
|
+
"plugin": plugin_plugin_spec,
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
return plugin_specs
|
|
@@ -47,7 +47,9 @@ class CommandPluginsLoader:
|
|
|
47
47
|
self._loaded_command_paths: Dict[CommandPath, LoadedCommandPlugin] = {}
|
|
48
48
|
|
|
49
49
|
def register_builtin_plugins(self) -> None:
|
|
50
|
-
for plugin_name, plugin in
|
|
50
|
+
for plugin_name, plugin in sorted(
|
|
51
|
+
get_builtin_plugin_name_to_plugin_spec().items()
|
|
52
|
+
):
|
|
51
53
|
try:
|
|
52
54
|
self._plugin_manager.register(plugin=plugin, name=plugin_name)
|
|
53
55
|
except Exception as ex:
|
|
@@ -33,8 +33,8 @@ class CommandRegistrationConfig:
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class CommandsRegistrationWithCallbacks:
|
|
36
|
-
def __init__(self
|
|
37
|
-
self.
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self._plugin_config_manager = PluginConfigProvider()
|
|
38
38
|
self._callbacks_after_registration: List[Callable[[], None]] = []
|
|
39
39
|
self._commands_registration_config: CommandRegistrationConfig = (
|
|
40
40
|
CommandRegistrationConfig(enable_external_command_plugins=True)
|
|
@@ -58,7 +58,7 @@ class CommandsRegistrationWithCallbacks:
|
|
|
58
58
|
|
|
59
59
|
def _register_builtin_and_enabled_external_plugin_commands(self):
|
|
60
60
|
enabled_external_plugins = (
|
|
61
|
-
self.
|
|
61
|
+
self._plugin_config_manager.get_enabled_plugin_names()
|
|
62
62
|
)
|
|
63
63
|
loaded_command_plugins = load_builtin_and_external_command_plugins(
|
|
64
64
|
enabled_external_plugins
|
snowflake/cli/_app/printing.py
CHANGED
|
@@ -16,7 +16,7 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
import sys
|
|
19
|
-
from datetime import datetime
|
|
19
|
+
from datetime import date, datetime
|
|
20
20
|
from json import JSONEncoder
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from textwrap import indent
|
|
@@ -57,7 +57,7 @@ class CustomJSONEncoder(JSONEncoder):
|
|
|
57
57
|
return o.result
|
|
58
58
|
if isinstance(o, (CollectionResult, MultipleResults)):
|
|
59
59
|
return list(o.result)
|
|
60
|
-
if isinstance(o, datetime):
|
|
60
|
+
if isinstance(o, (date, datetime)):
|
|
61
61
|
return o.isoformat()
|
|
62
62
|
if isinstance(o, Path):
|
|
63
63
|
return str(o)
|
|
@@ -287,9 +287,7 @@ def add(
|
|
|
287
287
|
ConnectionConfig(**connection_options),
|
|
288
288
|
)
|
|
289
289
|
if set_as_default:
|
|
290
|
-
set_config_value(
|
|
291
|
-
section=None, key="default_connection_name", value=connection_name
|
|
292
|
-
)
|
|
290
|
+
set_config_value(path=["default_connection_name"], value=connection_name)
|
|
293
291
|
|
|
294
292
|
return MessageResult(
|
|
295
293
|
f"Wrote new connection {connection_name} to {connections_file}"
|
|
@@ -357,7 +355,7 @@ def set_default(
|
|
|
357
355
|
):
|
|
358
356
|
"""Changes default connection to provided value."""
|
|
359
357
|
get_connection_dict(connection_name=name)
|
|
360
|
-
set_config_value(
|
|
358
|
+
set_config_value(path=["default_connection_name"], value=name)
|
|
361
359
|
return MessageResult(f"Default connection set to: {name}")
|
|
362
360
|
|
|
363
361
|
|
|
@@ -49,7 +49,7 @@ def v1_to_v2(
|
|
|
49
49
|
accept_templates: bool = typer.Option(
|
|
50
50
|
False, "-t", "--accept-templates", help="Allows the migration of templates."
|
|
51
51
|
),
|
|
52
|
-
migrate_local_yml:
|
|
52
|
+
migrate_local_yml: Optional[bool] = typer.Option(
|
|
53
53
|
None,
|
|
54
54
|
"-l",
|
|
55
55
|
"--migrate-local-overrides/--no-migrate-local-overrides",
|
|
@@ -234,7 +234,7 @@ def _validate_imported_default_connection_name(
|
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
def _convert_connection_from_snowsql_config_section(
|
|
237
|
-
snowsql_connection: list[tuple[str, Any]]
|
|
237
|
+
snowsql_connection: list[tuple[str, Any]],
|
|
238
238
|
) -> dict[str, Any]:
|
|
239
239
|
from ast import literal_eval
|
|
240
240
|
|
|
@@ -290,7 +290,6 @@ def _validate_and_save_connections_imported_from_snowsql(
|
|
|
290
290
|
f"Setting [{default_cli_connection_name}] connection as Snowflake CLI's default connection."
|
|
291
291
|
)
|
|
292
292
|
set_config_value(
|
|
293
|
-
|
|
294
|
-
key="default_connection_name",
|
|
293
|
+
path=["default_connection_name"],
|
|
295
294
|
value=default_cli_connection_name,
|
|
296
295
|
)
|
|
@@ -21,9 +21,7 @@ from snowflake.cli._plugins.notebook.notebook_entity_model import NotebookEntity
|
|
|
21
21
|
from snowflake.cli._plugins.notebook.types import NotebookStagePath
|
|
22
22
|
from snowflake.cli._plugins.workspace.manager import WorkspaceManager
|
|
23
23
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
24
|
-
from snowflake.cli.api.commands.decorators import
|
|
25
|
-
with_project_definition,
|
|
26
|
-
)
|
|
24
|
+
from snowflake.cli.api.commands.decorators import with_project_definition
|
|
27
25
|
from snowflake.cli.api.commands.flags import (
|
|
28
26
|
ReplaceOption,
|
|
29
27
|
entity_argument,
|
|
@@ -107,7 +105,8 @@ def create(
|
|
|
107
105
|
def deploy(
|
|
108
106
|
entity_id: str = entity_argument("notebook"),
|
|
109
107
|
replace: bool = ReplaceOption(
|
|
110
|
-
help="Replace notebook object if it already exists."
|
|
108
|
+
help="Replace notebook object if it already exists. It only uploads new and overwrites existing files, "
|
|
109
|
+
"but does not remove any files already on the stage.",
|
|
111
110
|
),
|
|
112
111
|
**options,
|
|
113
112
|
) -> CommandResult:
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
from typing import Dict, List
|
|
19
|
+
|
|
20
|
+
import typer
|
|
21
|
+
from snowflake.cli._plugins.plugin.manager import PluginManager
|
|
22
|
+
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
23
|
+
from snowflake.cli.api.output.types import (
|
|
24
|
+
CollectionResult,
|
|
25
|
+
CommandResult,
|
|
26
|
+
MessageResult,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
log = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
app = SnowTyperFactory(
|
|
32
|
+
name="plugin",
|
|
33
|
+
help="Plugin management commands.",
|
|
34
|
+
is_hidden=lambda: True,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@app.command(name="enable", requires_connection=False)
|
|
39
|
+
def enable(
|
|
40
|
+
plugin_name: str = typer.Argument(None, help="Plugin name"),
|
|
41
|
+
**options,
|
|
42
|
+
) -> CommandResult:
|
|
43
|
+
"""Enables a plugin with a given name."""
|
|
44
|
+
plugin_manager = PluginManager()
|
|
45
|
+
plugin_manager.assert_plugin_is_installed(plugin_name)
|
|
46
|
+
plugin_manager.enable_plugin(plugin_name)
|
|
47
|
+
|
|
48
|
+
return MessageResult(f"Plugin {plugin_name} successfully enabled.")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@app.command(name="disable", requires_connection=False)
|
|
52
|
+
def disable(
|
|
53
|
+
plugin_name: str = typer.Argument(None, help="Plugin name"),
|
|
54
|
+
**options,
|
|
55
|
+
) -> CommandResult:
|
|
56
|
+
"""Disables a plugin with a given name."""
|
|
57
|
+
plugin_manager = PluginManager()
|
|
58
|
+
plugin_manager.assert_plugin_is_installed(plugin_name)
|
|
59
|
+
plugin_manager.disable_plugin(plugin_name)
|
|
60
|
+
|
|
61
|
+
return MessageResult(f"Plugin {plugin_name} successfully disabled.")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@app.command(name="list", requires_connection=False)
|
|
65
|
+
def list_(
|
|
66
|
+
**options,
|
|
67
|
+
) -> CommandResult:
|
|
68
|
+
"""Lists all installed plugins."""
|
|
69
|
+
plugin_manager = PluginManager()
|
|
70
|
+
result: List[Dict[str, str]] = []
|
|
71
|
+
for plugin_name in sorted(plugin_manager.get_installed_plugin_names()):
|
|
72
|
+
result.append(
|
|
73
|
+
{
|
|
74
|
+
"plugin name": plugin_name,
|
|
75
|
+
"enabled": plugin_manager.is_plugin_enabled(plugin_name),
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return CollectionResult(result)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import importlib
|
|
15
|
+
from typing import List
|
|
16
|
+
|
|
17
|
+
from snowflake.cli.api.config import (
|
|
18
|
+
PLUGIN_ENABLED_KEY,
|
|
19
|
+
PLUGINS_SECTION_PATH,
|
|
20
|
+
config_section_exists,
|
|
21
|
+
get_config_section,
|
|
22
|
+
set_config_value,
|
|
23
|
+
)
|
|
24
|
+
from snowflake.cli.api.exceptions import PluginNotInstalledError
|
|
25
|
+
from snowflake.cli.api.plugins.command import SNOWCLI_COMMAND_PLUGIN_NAMESPACE
|
|
26
|
+
from snowflake.cli.api.plugins.plugin_config import PluginConfigProvider
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PluginManager:
|
|
30
|
+
"""
|
|
31
|
+
Manage installation of plugins.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def enable_plugin(self, plugin_name: str):
|
|
35
|
+
self._change_plugin_enabled(plugin_name, enable=True)
|
|
36
|
+
|
|
37
|
+
def disable_plugin(self, plugin_name: str):
|
|
38
|
+
self._change_plugin_enabled(plugin_name, enable=False)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _change_plugin_enabled(plugin_name: str, enable: bool):
|
|
42
|
+
plugin_config_path = PLUGINS_SECTION_PATH + [plugin_name]
|
|
43
|
+
|
|
44
|
+
if config_section_exists(*plugin_config_path):
|
|
45
|
+
plugin_config = get_config_section(*plugin_config_path)
|
|
46
|
+
elif enable:
|
|
47
|
+
plugin_config = {}
|
|
48
|
+
else:
|
|
49
|
+
# do not add a new plugin config if user wants to disable a plugin which is not configured
|
|
50
|
+
# (plugins are disabled by default)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
plugin_config[PLUGIN_ENABLED_KEY] = enable
|
|
54
|
+
set_config_value(path=plugin_config_path, value=plugin_config)
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def is_plugin_enabled(plugin_name: str) -> bool:
|
|
58
|
+
return PluginConfigProvider.get_config(plugin_name).is_plugin_enabled
|
|
59
|
+
|
|
60
|
+
def assert_plugin_is_installed(self, plugin_name: str):
|
|
61
|
+
installed_plugins = self.get_installed_plugin_names()
|
|
62
|
+
if plugin_name not in installed_plugins:
|
|
63
|
+
raise PluginNotInstalledError(
|
|
64
|
+
plugin_name, installed_plugins=sorted(installed_plugins)
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def get_installed_plugin_names() -> List[str]:
|
|
69
|
+
return [
|
|
70
|
+
entry_point.name
|
|
71
|
+
for entry_point in importlib.metadata.entry_points(
|
|
72
|
+
group=SNOWCLI_COMMAND_PLUGIN_NAMESPACE
|
|
73
|
+
)
|
|
74
|
+
]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from snowflake.cli._plugins.plugin import commands
|
|
16
|
+
from snowflake.cli.api.plugins.command import (
|
|
17
|
+
SNOWCLI_ROOT_COMMAND_PATH,
|
|
18
|
+
CommandSpec,
|
|
19
|
+
CommandType,
|
|
20
|
+
plugin_hook_impl,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@plugin_hook_impl
|
|
25
|
+
def command_spec():
|
|
26
|
+
return CommandSpec(
|
|
27
|
+
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
|
|
28
|
+
command_type=CommandType.COMMAND_GROUP,
|
|
29
|
+
typer_instance=commands.app.create_instance(),
|
|
30
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import List, Optional
|
|
16
|
+
|
|
17
|
+
import typer
|
|
18
|
+
from snowflake.cli._plugins.project.feature_flags import FeatureFlag
|
|
19
|
+
from snowflake.cli._plugins.project.manager import ProjectManager
|
|
20
|
+
from snowflake.cli._plugins.project.project_entity_model import (
|
|
21
|
+
ProjectEntityModel,
|
|
22
|
+
)
|
|
23
|
+
from snowflake.cli._plugins.stage.manager import StageManager
|
|
24
|
+
from snowflake.cli.api.artifacts.upload import put_files
|
|
25
|
+
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
26
|
+
from snowflake.cli.api.commands.decorators import with_project_definition
|
|
27
|
+
from snowflake.cli.api.commands.flags import (
|
|
28
|
+
entity_argument,
|
|
29
|
+
identifier_argument,
|
|
30
|
+
variables_option,
|
|
31
|
+
)
|
|
32
|
+
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
33
|
+
from snowflake.cli.api.commands.utils import get_entity_for_operation
|
|
34
|
+
from snowflake.cli.api.console.console import cli_console
|
|
35
|
+
from snowflake.cli.api.identifiers import FQN
|
|
36
|
+
from snowflake.cli.api.output.types import MessageResult, SingleQueryResult
|
|
37
|
+
from snowflake.cli.api.project.project_paths import ProjectPaths
|
|
38
|
+
|
|
39
|
+
app = SnowTyperFactory(
|
|
40
|
+
name="project",
|
|
41
|
+
help="Manages projects in Snowflake.",
|
|
42
|
+
is_hidden=FeatureFlag.ENABLE_SNOWFLAKE_PROJECTS.is_disabled,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
project_identifier = identifier_argument(sf_object="project", example="MY_PROJECT")
|
|
46
|
+
version_flag = typer.Option(
|
|
47
|
+
..., "--version", help="Version of the project to use.", show_default=False
|
|
48
|
+
)
|
|
49
|
+
variables_flag = variables_option(
|
|
50
|
+
'Variables for the execution context; for example: `-D "<key>=<value>"`.'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@app.command(requires_connection=True)
|
|
55
|
+
def execute(
|
|
56
|
+
identifier: FQN = project_identifier,
|
|
57
|
+
version: str = version_flag,
|
|
58
|
+
variables: Optional[List[str]] = variables_flag,
|
|
59
|
+
**options,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Executes a project.
|
|
63
|
+
"""
|
|
64
|
+
result = ProjectManager().execute(
|
|
65
|
+
project_name=identifier, version=version, variables=variables
|
|
66
|
+
)
|
|
67
|
+
return SingleQueryResult(result)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.command(requires_connection=True)
|
|
71
|
+
def dry_run(
|
|
72
|
+
identifier: FQN = project_identifier,
|
|
73
|
+
version: str = version_flag,
|
|
74
|
+
variables: Optional[List[str]] = variables_flag,
|
|
75
|
+
**options,
|
|
76
|
+
):
|
|
77
|
+
"""
|
|
78
|
+
Validates a project.
|
|
79
|
+
"""
|
|
80
|
+
result = ProjectManager().execute(
|
|
81
|
+
project_name=identifier, version=version, dry_run=True, variables=variables
|
|
82
|
+
)
|
|
83
|
+
return SingleQueryResult(result)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@app.command(requires_connection=True)
|
|
87
|
+
@with_project_definition()
|
|
88
|
+
def create_version(
|
|
89
|
+
entity_id: str = entity_argument("project"),
|
|
90
|
+
**options,
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Upload local files and create a new version of a project using those files. If the stage does not exist, it will be created.
|
|
94
|
+
"""
|
|
95
|
+
cli_context = get_cli_context()
|
|
96
|
+
project: ProjectEntityModel = get_entity_for_operation(
|
|
97
|
+
cli_context=cli_context,
|
|
98
|
+
entity_id=entity_id,
|
|
99
|
+
project_definition=cli_context.project_definition,
|
|
100
|
+
entity_type="project",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Sync state
|
|
104
|
+
with cli_console.phase("Syncing project state"):
|
|
105
|
+
stage_name = FQN.from_stage(project.stage)
|
|
106
|
+
sm = StageManager()
|
|
107
|
+
|
|
108
|
+
cli_console.step(f"Creating stage {stage_name}")
|
|
109
|
+
sm.create(fqn=stage_name)
|
|
110
|
+
|
|
111
|
+
put_files(
|
|
112
|
+
project_paths=ProjectPaths(project_root=cli_context.project_root),
|
|
113
|
+
stage_root=project.stage,
|
|
114
|
+
artifacts=project.artifacts,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Create project and version
|
|
118
|
+
with cli_console.phase("Creating project and version"):
|
|
119
|
+
pm = ProjectManager()
|
|
120
|
+
cli_console.step(f"Creating project {project.fqn}")
|
|
121
|
+
pm.create(project_name=project.fqn)
|
|
122
|
+
|
|
123
|
+
cli_console.step(f"Creating version from stage {stage_name}")
|
|
124
|
+
pm.create_version(project_name=project.fqn, stage_name=stage_name)
|
|
125
|
+
return MessageResult(f"Project {project.fqn} deployed.")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@app.command(requires_connection=True)
|
|
129
|
+
@with_project_definition()
|
|
130
|
+
def add_version(
|
|
131
|
+
entity_id: str = entity_argument("project"),
|
|
132
|
+
_from: str = typer.Option(
|
|
133
|
+
...,
|
|
134
|
+
"--from",
|
|
135
|
+
help="Source stage to create the version from.",
|
|
136
|
+
show_default=False,
|
|
137
|
+
),
|
|
138
|
+
alias: str
|
|
139
|
+
| None = typer.Option(
|
|
140
|
+
None, "--alias", help="Alias for the version.", show_default=False
|
|
141
|
+
),
|
|
142
|
+
comment: str
|
|
143
|
+
| None = typer.Option(
|
|
144
|
+
None, "--comment", help="Version comment.", show_default=False
|
|
145
|
+
),
|
|
146
|
+
**options,
|
|
147
|
+
):
|
|
148
|
+
"""Adds a new version to a project using existing sources from provided stage path."""
|
|
149
|
+
|
|
150
|
+
pm = ProjectManager()
|
|
151
|
+
pm.add_version(
|
|
152
|
+
project_name=entity_id,
|
|
153
|
+
from_stage=_from,
|
|
154
|
+
alias=alias,
|
|
155
|
+
comment=comment,
|
|
156
|
+
)
|
|
157
|
+
return MessageResult("Version added.")
|
|
@@ -11,3 +11,12 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from enum import unique
|
|
16
|
+
|
|
17
|
+
from snowflake.cli.api.feature_flags import BooleanFlag, FeatureFlagMixin
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@unique
|
|
21
|
+
class FeatureFlag(FeatureFlagMixin):
|
|
22
|
+
ENABLE_SNOWFLAKE_PROJECTS = BooleanFlag("ENABLE_SNOWFLAKE_PROJECTS", False)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
from textwrap import dedent
|
|
15
|
+
from typing import List
|
|
16
|
+
|
|
17
|
+
from snowflake.cli._plugins.stage.manager import StageManager
|
|
18
|
+
from snowflake.cli.api.commands.utils import parse_key_value_variables
|
|
19
|
+
from snowflake.cli.api.identifiers import FQN
|
|
20
|
+
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
21
|
+
from snowflake.cli.api.stage_path import StagePath
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ProjectManager(SqlExecutionMixin):
|
|
25
|
+
def execute(
|
|
26
|
+
self,
|
|
27
|
+
project_name: FQN,
|
|
28
|
+
version: str | None = None,
|
|
29
|
+
variables: List[str] | None = None,
|
|
30
|
+
dry_run: bool = False,
|
|
31
|
+
):
|
|
32
|
+
query = f"EXECUTE PROJECT {project_name.sql_identifier}"
|
|
33
|
+
if variables:
|
|
34
|
+
query += StageManager.parse_execute_variables(
|
|
35
|
+
parse_key_value_variables(variables)
|
|
36
|
+
)
|
|
37
|
+
if version:
|
|
38
|
+
query += f" WITH VERSION {version}"
|
|
39
|
+
if dry_run:
|
|
40
|
+
query += " DRY_RUN=TRUE"
|
|
41
|
+
return self.execute_query(query=query)
|
|
42
|
+
|
|
43
|
+
def create(
|
|
44
|
+
self,
|
|
45
|
+
project_name: FQN,
|
|
46
|
+
) -> str:
|
|
47
|
+
queries = dedent(f"CREATE PROJECT IF NOT EXISTS {project_name.sql_identifier}")
|
|
48
|
+
return self.execute_query(query=queries)
|
|
49
|
+
|
|
50
|
+
def create_version(self, project_name: FQN, stage_name: FQN):
|
|
51
|
+
return self.add_version(
|
|
52
|
+
project_name=project_name,
|
|
53
|
+
from_stage=StagePath.from_stage_str(stage_name).absolute_path(
|
|
54
|
+
at_prefix=True
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def add_version(
|
|
59
|
+
self,
|
|
60
|
+
project_name: str | FQN,
|
|
61
|
+
from_stage: str,
|
|
62
|
+
alias: str | None = None,
|
|
63
|
+
comment: str | None = None,
|
|
64
|
+
):
|
|
65
|
+
project_name = (
|
|
66
|
+
project_name
|
|
67
|
+
if isinstance(project_name, FQN)
|
|
68
|
+
else FQN.from_string(project_name)
|
|
69
|
+
)
|
|
70
|
+
query = f"ALTER PROJECT {project_name.identifier} ADD VERSION"
|
|
71
|
+
if alias:
|
|
72
|
+
query += f" IF NOT EXIST {alias}"
|
|
73
|
+
query += f" FROM {from_stage}"
|
|
74
|
+
if comment:
|
|
75
|
+
query += f" COMMENT = '{comment}'"
|
|
76
|
+
return self.execute_query(query=query)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from snowflake.cli._plugins.project import commands
|
|
16
|
+
from snowflake.cli.api.plugins.command import (
|
|
17
|
+
SNOWCLI_ROOT_COMMAND_PATH,
|
|
18
|
+
CommandSpec,
|
|
19
|
+
CommandType,
|
|
20
|
+
plugin_hook_impl,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@plugin_hook_impl
|
|
25
|
+
def command_spec():
|
|
26
|
+
return CommandSpec(
|
|
27
|
+
parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
|
|
28
|
+
command_type=CommandType.COMMAND_GROUP,
|
|
29
|
+
typer_instance=commands.app.create_instance(),
|
|
30
|
+
)
|