snowflake-cli 3.10.0__py3-none-any.whl → 3.11.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/auth/__init__.py +13 -0
- snowflake/cli/_app/auth/errors.py +28 -0
- snowflake/cli/_app/auth/oidc_providers.py +393 -0
- snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -2
- snowflake/cli/_app/constants.py +10 -0
- snowflake/cli/_app/snow_connector.py +35 -0
- snowflake/cli/_plugins/auth/__init__.py +4 -2
- snowflake/cli/_plugins/auth/keypair/commands.py +2 -0
- snowflake/cli/_plugins/auth/oidc/__init__.py +13 -0
- snowflake/cli/_plugins/auth/oidc/commands.py +47 -0
- snowflake/cli/_plugins/auth/oidc/manager.py +66 -0
- snowflake/cli/_plugins/auth/oidc/plugin_spec.py +30 -0
- snowflake/cli/_plugins/connection/commands.py +37 -3
- snowflake/cli/_plugins/dbt/manager.py +7 -7
- snowflake/cli/_plugins/{project → dcm}/commands.py +113 -122
- snowflake/cli/_plugins/{project/project_entity_model.py → dcm/dcm_project_entity_model.py} +5 -5
- snowflake/cli/_plugins/dcm/manager.py +96 -0
- snowflake/cli/_plugins/{project → dcm}/plugin_spec.py +1 -1
- snowflake/cli/_plugins/notebook/notebook_entity.py +2 -0
- snowflake/cli/_plugins/notebook/notebook_entity_model.py +8 -1
- snowflake/cli/_plugins/object/command_aliases.py +16 -1
- snowflake/cli/_plugins/object/commands.py +27 -1
- snowflake/cli/_plugins/object/manager.py +12 -1
- snowflake/cli/_plugins/snowpark/commands.py +8 -1
- snowflake/cli/api/commands/decorators.py +7 -0
- snowflake/cli/api/commands/flags.py +26 -0
- snowflake/cli/api/config.py +24 -0
- snowflake/cli/api/connections.py +1 -0
- snowflake/cli/api/constants.py +2 -2
- snowflake/cli/api/project/schemas/entities/entities.py +6 -6
- snowflake/cli/api/rest_api.py +1 -0
- snowflake/cli/api/stage_path.py +4 -0
- snowflake/cli/api/utils/dict_utils.py +42 -1
- {snowflake_cli-3.10.0.dist-info → snowflake_cli-3.11.0.dist-info}/METADATA +13 -39
- {snowflake_cli-3.10.0.dist-info → snowflake_cli-3.11.0.dist-info}/RECORD +40 -33
- snowflake/cli/_plugins/project/manager.py +0 -134
- /snowflake/cli/_plugins/{project → dcm}/__init__.py +0 -0
- {snowflake_cli-3.10.0.dist-info → snowflake_cli-3.11.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.10.0.dist-info → snowflake_cli-3.11.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.10.0.dist-info → snowflake_cli-3.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
import logging
|
|
16
|
+
from typing import TypeAlias
|
|
17
|
+
|
|
18
|
+
from snowflake.cli._app.auth.errors import OidcProviderError
|
|
19
|
+
from snowflake.cli._app.auth.oidc_providers import (
|
|
20
|
+
OidcProviderType,
|
|
21
|
+
OidcProviderTypeWithAuto,
|
|
22
|
+
auto_detect_oidc_provider,
|
|
23
|
+
get_active_oidc_provider,
|
|
24
|
+
)
|
|
25
|
+
from snowflake.cli.api.exceptions import CliError
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
Providers: TypeAlias = OidcProviderType | OidcProviderTypeWithAuto
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class OidcManager:
|
|
34
|
+
"""
|
|
35
|
+
Manages OIDC authentication.
|
|
36
|
+
|
|
37
|
+
This class provides methods to read OIDC configurations for authentication.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def read_token(
|
|
41
|
+
self,
|
|
42
|
+
provider_type: Providers = OidcProviderTypeWithAuto.AUTO,
|
|
43
|
+
) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Reads OIDC token based on the specified provider type.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
provider_type: Type of provider to read token from ("auto" for auto-detection)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Token string or provider information
|
|
52
|
+
|
|
53
|
+
Raises:
|
|
54
|
+
CliError: If token reading fails
|
|
55
|
+
"""
|
|
56
|
+
logger.info("Reading OIDC token with provider type: %s", provider_type)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
if provider_type == OidcProviderTypeWithAuto.AUTO:
|
|
60
|
+
provider = auto_detect_oidc_provider()
|
|
61
|
+
else:
|
|
62
|
+
provider = get_active_oidc_provider(provider_type.value)
|
|
63
|
+
return provider.get_token()
|
|
64
|
+
except OidcProviderError as e:
|
|
65
|
+
logger.error("OIDC provider error: %s", str(e))
|
|
66
|
+
raise CliError(str(e))
|
|
@@ -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.auth import app
|
|
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=app.create_instance(),
|
|
30
|
+
)
|
|
@@ -53,6 +53,7 @@ from snowflake.cli.api.commands.flags import (
|
|
|
53
53
|
TokenFilePathOption,
|
|
54
54
|
UserOption,
|
|
55
55
|
WarehouseOption,
|
|
56
|
+
WorkloadIdentityProviderOption,
|
|
56
57
|
)
|
|
57
58
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
58
59
|
from snowflake.cli.api.config import (
|
|
@@ -62,7 +63,9 @@ from snowflake.cli.api.config import (
|
|
|
62
63
|
get_all_connections,
|
|
63
64
|
get_connection_dict,
|
|
64
65
|
get_default_connection_name,
|
|
66
|
+
remove_connection_from_proper_file,
|
|
65
67
|
set_config_value,
|
|
68
|
+
unset_config_value,
|
|
66
69
|
)
|
|
67
70
|
from snowflake.cli.api.console import cli_console
|
|
68
71
|
from snowflake.cli.api.constants import ObjectType
|
|
@@ -220,6 +223,12 @@ def add(
|
|
|
220
223
|
*AuthenticatorOption.param_decls,
|
|
221
224
|
help="Chosen authenticator, if other than password-based",
|
|
222
225
|
),
|
|
226
|
+
workload_identity_provider: Optional[str] = typer.Option(
|
|
227
|
+
None,
|
|
228
|
+
"-W",
|
|
229
|
+
*WorkloadIdentityProviderOption.param_decls,
|
|
230
|
+
help="Workload identity provider type",
|
|
231
|
+
),
|
|
223
232
|
private_key_file: Optional[str] = typer.Option(
|
|
224
233
|
None,
|
|
225
234
|
"--private-key",
|
|
@@ -256,6 +265,7 @@ def add(
|
|
|
256
265
|
"port": port,
|
|
257
266
|
"region": region,
|
|
258
267
|
"authenticator": authenticator,
|
|
268
|
+
"workload_identity_provider": workload_identity_provider,
|
|
259
269
|
"private_key_file": private_key_file,
|
|
260
270
|
"token_file_path": token_file_path,
|
|
261
271
|
}
|
|
@@ -317,6 +327,30 @@ def add(
|
|
|
317
327
|
)
|
|
318
328
|
|
|
319
329
|
|
|
330
|
+
@app.command(requires_connection=False)
|
|
331
|
+
def remove(
|
|
332
|
+
connection_name: str = typer.Argument(
|
|
333
|
+
help="Name of the connection to remove.",
|
|
334
|
+
show_default=False,
|
|
335
|
+
),
|
|
336
|
+
**options,
|
|
337
|
+
):
|
|
338
|
+
"""Removes a connection from configuration file."""
|
|
339
|
+
if not connection_exists(connection_name):
|
|
340
|
+
raise UsageError(f"Connection {connection_name} does not exist.")
|
|
341
|
+
|
|
342
|
+
is_default = get_default_connection_name() == connection_name
|
|
343
|
+
if is_default:
|
|
344
|
+
unset_config_value(path=["default_connection_name"])
|
|
345
|
+
|
|
346
|
+
connections_file = remove_connection_from_proper_file(connection_name)
|
|
347
|
+
|
|
348
|
+
return MessageResult(
|
|
349
|
+
f"Removed connection {connection_name} from {connections_file}."
|
|
350
|
+
f"{' It was the default connection, so default connection is now unset.' if is_default else ''}"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
320
354
|
@app.command(requires_connection=True)
|
|
321
355
|
def test(
|
|
322
356
|
**options,
|
|
@@ -355,9 +389,9 @@ def test(
|
|
|
355
389
|
"Host": conn.host,
|
|
356
390
|
"Account": conn.account,
|
|
357
391
|
"User": conn.user,
|
|
358
|
-
"Role": f
|
|
359
|
-
"Database": f
|
|
360
|
-
"Warehouse": f
|
|
392
|
+
"Role": f"{conn.role or 'not set'}",
|
|
393
|
+
"Database": f"{conn.database or 'not set'}",
|
|
394
|
+
"Warehouse": f"{conn.warehouse or 'not set'}",
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
if conn_ctx.enable_diag:
|
|
@@ -44,7 +44,7 @@ class DBTManager(SqlExecutionMixin):
|
|
|
44
44
|
|
|
45
45
|
def deploy(
|
|
46
46
|
self,
|
|
47
|
-
|
|
47
|
+
fqn: FQN,
|
|
48
48
|
path: SecurePath,
|
|
49
49
|
profiles_path: SecurePath,
|
|
50
50
|
force: bool,
|
|
@@ -66,7 +66,7 @@ class DBTManager(SqlExecutionMixin):
|
|
|
66
66
|
|
|
67
67
|
with cli_console.phase("Creating temporary stage"):
|
|
68
68
|
stage_manager = StageManager()
|
|
69
|
-
stage_fqn = FQN.from_string(f"dbt_{name}_stage").using_context()
|
|
69
|
+
stage_fqn = FQN.from_string(f"dbt_{fqn.name}_stage").using_context()
|
|
70
70
|
stage_name = stage_manager.get_standard_stage_prefix(stage_fqn)
|
|
71
71
|
stage_manager.create(stage_fqn, temporary=True)
|
|
72
72
|
|
|
@@ -86,11 +86,11 @@ class DBTManager(SqlExecutionMixin):
|
|
|
86
86
|
|
|
87
87
|
with cli_console.phase("Creating DBT project"):
|
|
88
88
|
if force is True:
|
|
89
|
-
query = f"CREATE OR REPLACE DBT PROJECT {
|
|
90
|
-
elif self.exists(name=
|
|
91
|
-
query = f"ALTER DBT PROJECT {
|
|
89
|
+
query = f"CREATE OR REPLACE DBT PROJECT {fqn}"
|
|
90
|
+
elif self.exists(name=fqn):
|
|
91
|
+
query = f"ALTER DBT PROJECT {fqn} ADD VERSION"
|
|
92
92
|
else:
|
|
93
|
-
query = f"CREATE DBT PROJECT {
|
|
93
|
+
query = f"CREATE DBT PROJECT {fqn}"
|
|
94
94
|
query += f"\nFROM {stage_name}"
|
|
95
95
|
return self.execute_query(query)
|
|
96
96
|
|
|
@@ -174,7 +174,7 @@ class DBTManager(SqlExecutionMixin):
|
|
|
174
174
|
yaml.safe_dump(yaml.safe_load(sfd), tfd)
|
|
175
175
|
|
|
176
176
|
def execute(
|
|
177
|
-
self, dbt_command: str, name:
|
|
177
|
+
self, dbt_command: str, name: FQN, run_async: bool, *dbt_cli_args
|
|
178
178
|
) -> SnowflakeCursor:
|
|
179
179
|
if dbt_cli_args:
|
|
180
180
|
dbt_command = " ".join([dbt_command, *dbt_cli_args]).strip()
|
|
@@ -11,24 +11,23 @@
|
|
|
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
14
|
from typing import List, Optional
|
|
16
15
|
|
|
17
16
|
import typer
|
|
17
|
+
from snowflake.cli._plugins.dcm.dcm_project_entity_model import (
|
|
18
|
+
DCMProjectEntityModel,
|
|
19
|
+
)
|
|
20
|
+
from snowflake.cli._plugins.dcm.manager import DCMProjectManager
|
|
18
21
|
from snowflake.cli._plugins.object.command_aliases import add_object_command_aliases
|
|
19
22
|
from snowflake.cli._plugins.object.commands import scope_option
|
|
20
23
|
from snowflake.cli._plugins.object.manager import ObjectManager
|
|
21
|
-
from snowflake.cli.
|
|
22
|
-
from snowflake.cli._plugins.project.project_entity_model import (
|
|
23
|
-
ProjectEntityModel,
|
|
24
|
-
)
|
|
24
|
+
from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
|
|
25
25
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
26
26
|
from snowflake.cli.api.commands.decorators import with_project_definition
|
|
27
27
|
from snowflake.cli.api.commands.flags import (
|
|
28
28
|
IfExistsOption,
|
|
29
29
|
IfNotExistsOption,
|
|
30
30
|
OverrideableOption,
|
|
31
|
-
PruneOption,
|
|
32
31
|
entity_argument,
|
|
33
32
|
identifier_argument,
|
|
34
33
|
like_option,
|
|
@@ -46,99 +45,130 @@ from snowflake.cli.api.output.types import (
|
|
|
46
45
|
QueryJsonValueResult,
|
|
47
46
|
QueryResult,
|
|
48
47
|
)
|
|
48
|
+
from snowflake.cli.api.project.project_paths import ProjectPaths
|
|
49
49
|
|
|
50
50
|
app = SnowTyperFactory(
|
|
51
|
-
name="
|
|
52
|
-
help="Manages
|
|
51
|
+
name="dcm",
|
|
52
|
+
help="Manages DCM Projects in Snowflake.",
|
|
53
53
|
is_hidden=FeatureFlag.ENABLE_SNOWFLAKE_PROJECTS.is_disabled,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
version_flag = typer.Option(
|
|
58
|
-
None,
|
|
59
|
-
"--version",
|
|
60
|
-
help="Version of the project to use. If not specified default version is used. For names containing '$', use single quotes to prevent shell expansion (e.g., 'VERSION$1').",
|
|
61
|
-
show_default=False,
|
|
62
|
-
)
|
|
56
|
+
dcm_identifier = identifier_argument(sf_object="DCM Project", example="MY_PROJECT")
|
|
63
57
|
variables_flag = variables_option(
|
|
64
58
|
'Variables for the execution context; for example: `-D "<key>=<value>"`.'
|
|
65
59
|
)
|
|
66
60
|
configuration_flag = typer.Option(
|
|
67
61
|
None,
|
|
68
62
|
"--configuration",
|
|
69
|
-
help="Configuration of the
|
|
63
|
+
help="Configuration of the DCM Project to use. If not specified default configuration is used.",
|
|
70
64
|
show_default=False,
|
|
71
65
|
)
|
|
72
66
|
from_option = OverrideableOption(
|
|
73
67
|
None,
|
|
74
68
|
"--from",
|
|
69
|
+
mutually_exclusive=["prune"],
|
|
70
|
+
show_default=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
prune_option = OverrideableOption(
|
|
74
|
+
False,
|
|
75
|
+
"--prune",
|
|
76
|
+
help="Remove unused artifacts from the stage during sync. Mutually exclusive with --from.",
|
|
77
|
+
mutually_exclusive=["from_stage"],
|
|
78
|
+
show_default=False,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
alias_option = typer.Option(
|
|
82
|
+
None,
|
|
83
|
+
"--alias",
|
|
84
|
+
help="Alias for the deployment.",
|
|
85
|
+
show_default=False,
|
|
86
|
+
)
|
|
87
|
+
output_path_option = OverrideableOption(
|
|
88
|
+
None,
|
|
89
|
+
"--output-path",
|
|
90
|
+
show_default=False,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
terse_option = typer.Option(
|
|
94
|
+
False,
|
|
95
|
+
"--terse",
|
|
96
|
+
help="Returns only a subset of output columns.",
|
|
97
|
+
show_default=False,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
limit_option = typer.Option(
|
|
101
|
+
None,
|
|
102
|
+
"--limit",
|
|
103
|
+
help="Limits the maximum number of rows returned.",
|
|
75
104
|
show_default=False,
|
|
76
105
|
)
|
|
77
106
|
|
|
78
107
|
|
|
79
108
|
add_object_command_aliases(
|
|
80
109
|
app=app,
|
|
81
|
-
object_type=ObjectType.
|
|
82
|
-
name_argument=
|
|
110
|
+
object_type=ObjectType.DCM_PROJECT,
|
|
111
|
+
name_argument=dcm_identifier,
|
|
83
112
|
like_option=like_option(
|
|
84
|
-
help_example='`list --like "my%"` lists all
|
|
113
|
+
help_example='`list --like "my%"` lists all DCM Projects that begin with "my"'
|
|
85
114
|
),
|
|
86
115
|
scope_option=scope_option(help_example="`list --in database my_db`"),
|
|
87
|
-
ommit_commands=["create"
|
|
116
|
+
ommit_commands=["create"],
|
|
117
|
+
terse_option=terse_option,
|
|
118
|
+
limit_option=limit_option,
|
|
88
119
|
)
|
|
89
120
|
|
|
90
121
|
|
|
91
122
|
@app.command(requires_connection=True)
|
|
92
|
-
def
|
|
93
|
-
identifier: FQN =
|
|
94
|
-
version: Optional[str] = version_flag,
|
|
123
|
+
def deploy(
|
|
124
|
+
identifier: FQN = dcm_identifier,
|
|
95
125
|
from_stage: Optional[str] = from_option(
|
|
96
|
-
help="
|
|
126
|
+
help="Deploy DCM Project deployment from a given stage."
|
|
97
127
|
),
|
|
98
128
|
variables: Optional[List[str]] = variables_flag,
|
|
99
129
|
configuration: Optional[str] = configuration_flag,
|
|
130
|
+
alias: Optional[str] = alias_option,
|
|
131
|
+
prune: bool = prune_option(),
|
|
100
132
|
**options,
|
|
101
133
|
):
|
|
102
134
|
"""
|
|
103
|
-
|
|
135
|
+
Applies changes defined in DCM Project to Snowflake.
|
|
104
136
|
"""
|
|
105
|
-
|
|
106
|
-
raise CliError("--version and --from are mutually exclusive.")
|
|
107
|
-
|
|
108
|
-
result = ProjectManager().execute(
|
|
137
|
+
result = DCMProjectManager().execute(
|
|
109
138
|
project_name=identifier,
|
|
110
139
|
configuration=configuration,
|
|
111
|
-
|
|
112
|
-
from_stage=from_stage,
|
|
140
|
+
from_stage=from_stage if from_stage else _sync_local_files(prune=prune),
|
|
113
141
|
variables=variables,
|
|
142
|
+
alias=alias,
|
|
143
|
+
output_path=None,
|
|
114
144
|
)
|
|
115
145
|
return QueryJsonValueResult(result)
|
|
116
146
|
|
|
117
147
|
|
|
118
148
|
@app.command(requires_connection=True)
|
|
119
|
-
def
|
|
120
|
-
identifier: FQN =
|
|
121
|
-
version: Optional[str] = version_flag,
|
|
149
|
+
def plan(
|
|
150
|
+
identifier: FQN = dcm_identifier,
|
|
122
151
|
from_stage: Optional[str] = from_option(
|
|
123
|
-
help="
|
|
152
|
+
help="Plan DCM Project deployment from a given stage."
|
|
124
153
|
),
|
|
125
154
|
variables: Optional[List[str]] = variables_flag,
|
|
126
155
|
configuration: Optional[str] = configuration_flag,
|
|
156
|
+
prune: bool = prune_option(),
|
|
157
|
+
output_path: Optional[str] = output_path_option(
|
|
158
|
+
help="Stage path where the deployment plan output will be stored."
|
|
159
|
+
),
|
|
127
160
|
**options,
|
|
128
161
|
):
|
|
129
162
|
"""
|
|
130
|
-
|
|
163
|
+
Plans a DCM Project deployment (validates without executing).
|
|
131
164
|
"""
|
|
132
|
-
|
|
133
|
-
raise CliError("--version and --from are mutually exclusive.")
|
|
134
|
-
|
|
135
|
-
result = ProjectManager().execute(
|
|
165
|
+
result = DCMProjectManager().execute(
|
|
136
166
|
project_name=identifier,
|
|
137
167
|
configuration=configuration,
|
|
138
|
-
|
|
139
|
-
from_stage=from_stage,
|
|
168
|
+
from_stage=from_stage if from_stage else _sync_local_files(prune=prune),
|
|
140
169
|
dry_run=True,
|
|
141
170
|
variables=variables,
|
|
171
|
+
output_path=output_path,
|
|
142
172
|
)
|
|
143
173
|
return QueryJsonValueResult(result)
|
|
144
174
|
|
|
@@ -146,114 +176,55 @@ def dry_run(
|
|
|
146
176
|
@app.command(requires_connection=True)
|
|
147
177
|
@with_project_definition()
|
|
148
178
|
def create(
|
|
149
|
-
entity_id: str = entity_argument("
|
|
150
|
-
no_version: bool = typer.Option(
|
|
151
|
-
False,
|
|
152
|
-
"--no-version",
|
|
153
|
-
help="Do not initialize project with a new version, only create the snowflake object.",
|
|
154
|
-
),
|
|
179
|
+
entity_id: str = entity_argument("dcm"),
|
|
155
180
|
if_not_exists: bool = IfNotExistsOption(
|
|
156
181
|
help="Do nothing if the project already exists."
|
|
157
182
|
),
|
|
158
183
|
**options,
|
|
159
184
|
):
|
|
160
185
|
"""
|
|
161
|
-
Creates a
|
|
162
|
-
By default, the project is initialized with a new version created from local files.
|
|
186
|
+
Creates a DCM Project in Snowflake.
|
|
163
187
|
"""
|
|
164
188
|
cli_context = get_cli_context()
|
|
165
|
-
project:
|
|
189
|
+
project: DCMProjectEntityModel = get_entity_for_operation(
|
|
166
190
|
cli_context=cli_context,
|
|
167
191
|
entity_id=entity_id,
|
|
168
192
|
project_definition=cli_context.project_definition,
|
|
169
|
-
entity_type="
|
|
193
|
+
entity_type="dcm",
|
|
170
194
|
)
|
|
171
195
|
om = ObjectManager()
|
|
172
|
-
if om.object_exists(object_type="
|
|
173
|
-
message = f"Project '{project.fqn}' already exists."
|
|
196
|
+
if om.object_exists(object_type="dcm", fqn=project.fqn):
|
|
197
|
+
message = f"DCM Project '{project.fqn}' already exists."
|
|
174
198
|
if if_not_exists:
|
|
175
199
|
return MessageResult(message)
|
|
176
200
|
raise CliError(message)
|
|
177
201
|
|
|
178
|
-
if
|
|
179
|
-
object_type="stage", fqn=FQN.from_stage(project.stage)
|
|
180
|
-
):
|
|
202
|
+
if om.object_exists(object_type="stage", fqn=FQN.from_stage(project.stage)):
|
|
181
203
|
raise CliError(f"Stage '{project.stage}' already exists.")
|
|
182
204
|
|
|
183
|
-
|
|
184
|
-
with cli_console.phase(f"Creating
|
|
185
|
-
|
|
205
|
+
dpm = DCMProjectManager()
|
|
206
|
+
with cli_console.phase(f"Creating DCM Project '{project.fqn}'"):
|
|
207
|
+
dpm.create(project=project)
|
|
186
208
|
|
|
187
|
-
|
|
188
|
-
return MessageResult(f"Project '{project.fqn}' successfully created.")
|
|
189
|
-
return MessageResult(
|
|
190
|
-
f"Project '{project.fqn}' successfully created and initial version is added."
|
|
191
|
-
)
|
|
209
|
+
return MessageResult(f"DCM Project '{project.fqn}' successfully created.")
|
|
192
210
|
|
|
193
211
|
|
|
194
212
|
@app.command(requires_connection=True)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
entity_id: str = entity_argument("project"),
|
|
198
|
-
_from: Optional[str] = from_option(
|
|
199
|
-
help="Create a new version using given stage instead of uploading local files."
|
|
200
|
-
),
|
|
201
|
-
_alias: Optional[str] = typer.Option(
|
|
202
|
-
None, "--alias", help="Alias for the version.", show_default=False
|
|
203
|
-
),
|
|
204
|
-
comment: Optional[str] = typer.Option(
|
|
205
|
-
None, "--comment", help="Version comment.", show_default=False
|
|
206
|
-
),
|
|
207
|
-
prune: bool = PruneOption(default=True),
|
|
208
|
-
**options,
|
|
209
|
-
):
|
|
210
|
-
"""Uploads local files to Snowflake and cerates a new project version."""
|
|
211
|
-
if _from is not None and prune:
|
|
212
|
-
cli_console.warning(
|
|
213
|
-
"When `--from` option is used, `--prune` option will be ignored and files from stage will be used as they are."
|
|
214
|
-
)
|
|
215
|
-
prune = False
|
|
216
|
-
cli_context = get_cli_context()
|
|
217
|
-
project: ProjectEntityModel = get_entity_for_operation(
|
|
218
|
-
cli_context=cli_context,
|
|
219
|
-
entity_id=entity_id,
|
|
220
|
-
project_definition=cli_context.project_definition,
|
|
221
|
-
entity_type="project",
|
|
222
|
-
)
|
|
223
|
-
om = ObjectManager()
|
|
224
|
-
if not om.object_exists(object_type="project", fqn=project.fqn):
|
|
225
|
-
raise CliError(
|
|
226
|
-
f"Project '{project.fqn}' does not exist. Use `project create` command first."
|
|
227
|
-
)
|
|
228
|
-
ProjectManager().add_version(
|
|
229
|
-
project=project,
|
|
230
|
-
prune=prune,
|
|
231
|
-
from_stage=_from,
|
|
232
|
-
alias=_alias,
|
|
233
|
-
comment=comment,
|
|
234
|
-
)
|
|
235
|
-
alias_str = "" if _alias is None else f"'{_alias}' "
|
|
236
|
-
return MessageResult(
|
|
237
|
-
f"New project version {alias_str}added to project '{project.fqn}'."
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@app.command(requires_connection=True)
|
|
242
|
-
def list_versions(
|
|
243
|
-
identifier: FQN = project_identifier,
|
|
213
|
+
def list_deployments(
|
|
214
|
+
identifier: FQN = dcm_identifier,
|
|
244
215
|
**options,
|
|
245
216
|
):
|
|
246
217
|
"""
|
|
247
|
-
Lists
|
|
218
|
+
Lists deployments of given DCM Project.
|
|
248
219
|
"""
|
|
249
|
-
pm =
|
|
220
|
+
pm = DCMProjectManager()
|
|
250
221
|
results = pm.list_versions(project_name=identifier)
|
|
251
222
|
return QueryResult(results)
|
|
252
223
|
|
|
253
224
|
|
|
254
225
|
@app.command(requires_connection=True)
|
|
255
|
-
def
|
|
256
|
-
identifier: FQN =
|
|
226
|
+
def drop_deployment(
|
|
227
|
+
identifier: FQN = dcm_identifier,
|
|
257
228
|
version_name: str = typer.Argument(
|
|
258
229
|
help="Name or alias of the version to drop. For names containing '$', use single quotes to prevent shell expansion (e.g., 'VERSION$1').",
|
|
259
230
|
show_default=False,
|
|
@@ -262,7 +233,7 @@ def drop_version(
|
|
|
262
233
|
**options,
|
|
263
234
|
):
|
|
264
235
|
"""
|
|
265
|
-
Drops a version from the
|
|
236
|
+
Drops a version from the DCM Project.
|
|
266
237
|
"""
|
|
267
238
|
# Detect potential shell expansion issues
|
|
268
239
|
if version_name and version_name.upper() == "VERSION":
|
|
@@ -271,12 +242,32 @@ def drop_version(
|
|
|
271
242
|
f"If you meant to use a version like 'VERSION$1', try using single quotes: 'VERSION$1'."
|
|
272
243
|
)
|
|
273
244
|
|
|
274
|
-
|
|
275
|
-
|
|
245
|
+
dpm = DCMProjectManager()
|
|
246
|
+
dpm.drop_deployment(
|
|
276
247
|
project_name=identifier,
|
|
277
248
|
version_name=version_name,
|
|
278
249
|
if_exists=if_exists,
|
|
279
250
|
)
|
|
280
251
|
return MessageResult(
|
|
281
|
-
f"Version '{version_name}' dropped from
|
|
252
|
+
f"Version '{version_name}' dropped from DCM Project '{identifier}'."
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _sync_local_files(prune: bool = False) -> str:
|
|
257
|
+
cli_context = get_cli_context()
|
|
258
|
+
project_entity = get_entity_for_operation(
|
|
259
|
+
cli_context=cli_context,
|
|
260
|
+
entity_id=None,
|
|
261
|
+
project_definition=cli_context.project_definition,
|
|
262
|
+
entity_type="dcm",
|
|
282
263
|
)
|
|
264
|
+
|
|
265
|
+
with cli_console.phase("Syncing local files to stage"):
|
|
266
|
+
sync_artifacts_with_stage(
|
|
267
|
+
project_paths=ProjectPaths(project_root=cli_context.project_root),
|
|
268
|
+
stage_root=project_entity.stage,
|
|
269
|
+
artifacts=project_entity.artifacts,
|
|
270
|
+
prune=prune,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return project_entity.stage
|
|
@@ -35,10 +35,10 @@ T = TypeVar("T")
|
|
|
35
35
|
MANIFEST_FILE_NAME = "manifest.yml"
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class
|
|
39
|
-
type: Literal["
|
|
38
|
+
class DCMProjectEntityModel(EntityModelBaseWithArtifacts):
|
|
39
|
+
type: Literal["dcm"] = DiscriminatorField() # noqa: A003
|
|
40
40
|
stage: Optional[str] = Field(
|
|
41
|
-
title="Stage in which the
|
|
41
|
+
title="Stage in which the DCM Project artifacts will be stored", default=None
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
@field_validator("artifacts")
|
|
@@ -54,6 +54,6 @@ class ProjectEntityModel(EntityModelBaseWithArtifacts):
|
|
|
54
54
|
return super().transform_artifacts(orig_artifacts)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
@attach_spans_to_entity_actions(entity_name="
|
|
58
|
-
class
|
|
57
|
+
@attach_spans_to_entity_actions(entity_name="dcm")
|
|
58
|
+
class DCMProjectEntity(EntityBase[DCMProjectEntityModel]):
|
|
59
59
|
"""Placeholder for project entity"""
|