snowflake-cli 3.10.1__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.
Files changed (32) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/auth/__init__.py +13 -0
  3. snowflake/cli/_app/auth/errors.py +28 -0
  4. snowflake/cli/_app/auth/oidc_providers.py +393 -0
  5. snowflake/cli/_app/constants.py +10 -0
  6. snowflake/cli/_app/snow_connector.py +35 -0
  7. snowflake/cli/_plugins/auth/__init__.py +4 -2
  8. snowflake/cli/_plugins/auth/keypair/commands.py +2 -0
  9. snowflake/cli/_plugins/auth/oidc/__init__.py +13 -0
  10. snowflake/cli/_plugins/auth/oidc/commands.py +47 -0
  11. snowflake/cli/_plugins/auth/oidc/manager.py +66 -0
  12. snowflake/cli/_plugins/auth/oidc/plugin_spec.py +30 -0
  13. snowflake/cli/_plugins/connection/commands.py +37 -3
  14. snowflake/cli/_plugins/dbt/manager.py +1 -3
  15. snowflake/cli/_plugins/dcm/commands.py +79 -88
  16. snowflake/cli/_plugins/dcm/manager.py +17 -57
  17. snowflake/cli/_plugins/notebook/notebook_entity.py +2 -0
  18. snowflake/cli/_plugins/notebook/notebook_entity_model.py +8 -1
  19. snowflake/cli/_plugins/object/command_aliases.py +16 -1
  20. snowflake/cli/_plugins/object/commands.py +27 -1
  21. snowflake/cli/_plugins/object/manager.py +12 -1
  22. snowflake/cli/_plugins/snowpark/commands.py +8 -1
  23. snowflake/cli/api/commands/decorators.py +7 -0
  24. snowflake/cli/api/commands/flags.py +26 -0
  25. snowflake/cli/api/config.py +24 -0
  26. snowflake/cli/api/connections.py +1 -0
  27. snowflake/cli/api/utils/dict_utils.py +42 -1
  28. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/METADATA +12 -38
  29. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/RECORD +32 -25
  30. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/WHEEL +0 -0
  31. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/entry_points.txt +0 -0
  32. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -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'{conn.role or "not set"}',
359
- "Database": f'{conn.database or "not set"}',
360
- "Warehouse": f'{conn.warehouse or "not set"}',
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:
@@ -26,7 +26,6 @@ from snowflake.cli.api.console import cli_console
26
26
  from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, ObjectType
27
27
  from snowflake.cli.api.exceptions import CliError
28
28
  from snowflake.cli.api.identifiers import FQN
29
- from snowflake.cli.api.project.util import unquote_identifier
30
29
  from snowflake.cli.api.secure_path import SecurePath
31
30
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
32
31
  from snowflake.connector.cursor import SnowflakeCursor
@@ -67,8 +66,7 @@ class DBTManager(SqlExecutionMixin):
67
66
 
68
67
  with cli_console.phase("Creating temporary stage"):
69
68
  stage_manager = StageManager()
70
- unquoted_name = unquote_identifier(fqn.name)
71
- stage_fqn = FQN.from_string(f"DBT_{unquoted_name}_STAGE").using_context()
69
+ stage_fqn = FQN.from_string(f"dbt_{fqn.name}_stage").using_context()
72
70
  stage_name = stage_manager.get_standard_stage_prefix(stage_fqn)
73
71
  stage_manager.create(stage_fqn, temporary=True)
74
72
 
@@ -11,7 +11,6 @@
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
@@ -22,13 +21,13 @@ from snowflake.cli._plugins.dcm.manager import DCMProjectManager
22
21
  from snowflake.cli._plugins.object.command_aliases import add_object_command_aliases
23
22
  from snowflake.cli._plugins.object.commands import scope_option
24
23
  from snowflake.cli._plugins.object.manager import ObjectManager
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,6 +45,7 @@ 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
51
  name="dcm",
@@ -54,12 +54,6 @@ app = SnowTyperFactory(
54
54
  )
55
55
 
56
56
  dcm_identifier = identifier_argument(sf_object="DCM Project", example="MY_PROJECT")
57
- version_flag = typer.Option(
58
- None,
59
- "--version",
60
- help="Version of the DCM 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
- )
63
57
  variables_flag = variables_option(
64
58
  'Variables for the execution context; for example: `-D "<key>=<value>"`.'
65
59
  )
@@ -72,6 +66,41 @@ configuration_flag = typer.Option(
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
 
@@ -85,32 +114,33 @@ add_object_command_aliases(
85
114
  ),
86
115
  scope_option=scope_option(help_example="`list --in database my_db`"),
87
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
123
  def deploy(
93
124
  identifier: FQN = dcm_identifier,
94
- version: Optional[str] = version_flag,
95
125
  from_stage: Optional[str] = from_option(
96
- help="Apply changes defined in given stage instead of using a specific project version."
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
- if version and from_stage:
106
- raise CliError("--version and --from are mutually exclusive.")
107
-
108
137
  result = DCMProjectManager().execute(
109
138
  project_name=identifier,
110
139
  configuration=configuration,
111
- version=version,
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
 
@@ -118,27 +148,27 @@ def deploy(
118
148
  @app.command(requires_connection=True)
119
149
  def plan(
120
150
  identifier: FQN = dcm_identifier,
121
- version: Optional[str] = version_flag,
122
151
  from_stage: Optional[str] = from_option(
123
- help="Plan DCM Project deployment from given stage instead of using a specific version."
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
- if version and from_stage:
133
- raise CliError("--version and --from are mutually exclusive.")
134
-
135
165
  result = DCMProjectManager().execute(
136
166
  project_name=identifier,
137
167
  configuration=configuration,
138
- version=version,
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
 
@@ -147,11 +177,6 @@ def plan(
147
177
  @with_project_definition()
148
178
  def create(
149
179
  entity_id: str = entity_argument("dcm"),
150
- no_version: bool = typer.Option(
151
- False,
152
- "--no-version",
153
- help="Do not initialize DCM Project with a new version, only create the snowflake object.",
154
- ),
155
180
  if_not_exists: bool = IfNotExistsOption(
156
181
  help="Do nothing if the project already exists."
157
182
  ),
@@ -159,7 +184,6 @@ def create(
159
184
  ):
160
185
  """
161
186
  Creates a DCM Project in Snowflake.
162
- By default, the DCM Project is initialized with a new version created from local files.
163
187
  """
164
188
  cli_context = get_cli_context()
165
189
  project: DCMProjectEntityModel = get_entity_for_operation(
@@ -175,76 +199,23 @@ def create(
175
199
  return MessageResult(message)
176
200
  raise CliError(message)
177
201
 
178
- if not no_version and om.object_exists(
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
205
  dpm = DCMProjectManager()
184
206
  with cli_console.phase(f"Creating DCM Project '{project.fqn}'"):
185
- dpm.create(project=project, initialize_version_from_local_files=not no_version)
207
+ dpm.create(project=project)
186
208
 
187
- if no_version:
188
- return MessageResult(f"DCM Project '{project.fqn}' successfully created.")
189
- return MessageResult(
190
- f"DCM 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
- @with_project_definition()
196
- def add_version(
197
- entity_id: str = entity_argument("dcm"),
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 DCM 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: DCMProjectEntityModel = get_entity_for_operation(
218
- cli_context=cli_context,
219
- entity_id=entity_id,
220
- project_definition=cli_context.project_definition,
221
- entity_type="dcm",
222
- )
223
- om = ObjectManager()
224
- if not om.object_exists(object_type="dcm", fqn=project.fqn):
225
- raise CliError(
226
- f"DCM Project '{project.fqn}' does not exist. Use `dcm create` command first."
227
- )
228
- DCMProjectManager().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 version {alias_str}added to DCM Project '{project.fqn}'."
238
- )
239
-
240
-
241
- @app.command(requires_connection=True)
242
- def list_versions(
213
+ def list_deployments(
243
214
  identifier: FQN = dcm_identifier,
244
215
  **options,
245
216
  ):
246
217
  """
247
- Lists versions of given DCM Project.
218
+ Lists deployments of given DCM Project.
248
219
  """
249
220
  pm = DCMProjectManager()
250
221
  results = pm.list_versions(project_name=identifier)
@@ -252,7 +223,7 @@ def list_versions(
252
223
 
253
224
 
254
225
  @app.command(requires_connection=True)
255
- def drop_version(
226
+ def drop_deployment(
256
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').",
@@ -272,7 +243,7 @@ def drop_version(
272
243
  )
273
244
 
274
245
  dpm = DCMProjectManager()
275
- dpm.drop_version(
246
+ dpm.drop_deployment(
276
247
  project_name=identifier,
277
248
  version_name=version_name,
278
249
  if_exists=if_exists,
@@ -280,3 +251,23 @@ def drop_version(
280
251
  return MessageResult(
281
252
  f"Version '{version_name}' dropped from DCM Project '{identifier}'."
282
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",
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
@@ -11,37 +11,36 @@
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
- from textwrap import dedent
15
- from typing import List, Optional
14
+
15
+ from typing import List
16
16
 
17
17
  from snowflake.cli._plugins.dcm.dcm_project_entity_model import DCMProjectEntityModel
18
18
  from snowflake.cli._plugins.stage.manager import StageManager
19
- from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
20
- from snowflake.cli.api.cli_global_context import get_cli_context
21
19
  from snowflake.cli.api.commands.utils import parse_key_value_variables
22
- from snowflake.cli.api.console.console import cli_console
23
20
  from snowflake.cli.api.identifiers import FQN
24
- from snowflake.cli.api.project.project_paths import ProjectPaths
25
21
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
26
22
  from snowflake.cli.api.stage_path import StagePath
27
- from snowflake.connector.cursor import SnowflakeCursor
28
23
 
29
24
 
30
25
  class DCMProjectManager(SqlExecutionMixin):
31
26
  def execute(
32
27
  self,
33
28
  project_name: FQN,
29
+ from_stage: str,
34
30
  configuration: str | None = None,
35
- version: str | None = None,
36
- from_stage: str | None = None,
37
31
  variables: List[str] | None = None,
38
32
  dry_run: bool = False,
33
+ alias: str | None = None,
34
+ output_path: str | None = None,
39
35
  ):
36
+
40
37
  query = f"EXECUTE DCM PROJECT {project_name.sql_identifier}"
41
38
  if dry_run:
42
39
  query += " PLAN"
43
40
  else:
44
41
  query += " DEPLOY"
42
+ if alias:
43
+ query += f" AS {alias}"
45
44
  if configuration or variables:
46
45
  query += f" USING"
47
46
  if configuration:
@@ -50,23 +49,16 @@ class DCMProjectManager(SqlExecutionMixin):
50
49
  query += StageManager.parse_execute_variables(
51
50
  parse_key_value_variables(variables)
52
51
  ).removeprefix(" using")
53
- if version:
54
- query += f" WITH VERSION {version}"
55
- elif from_stage:
56
- stage_path = StagePath.from_stage_str(from_stage)
57
- query += f" FROM {stage_path.absolute_path()}"
52
+ stage_path = StagePath.from_stage_str(from_stage)
53
+ query += f" FROM {stage_path.absolute_path()}"
54
+ if output_path:
55
+ output_stage_path = StagePath.from_stage_str(output_path)
56
+ query += f" OUTPUT_PATH {output_stage_path.absolute_path()}"
58
57
  return self.execute_query(query=query)
59
58
 
60
- def _create_object(self, project_name: FQN) -> SnowflakeCursor:
61
- query = dedent(f"CREATE DCM PROJECT {project_name.sql_identifier}")
62
- return self.execute_query(query)
63
-
64
- def create(
65
- self, project: DCMProjectEntityModel, initialize_version_from_local_files: bool
66
- ) -> None:
67
- self._create_object(project.fqn)
68
- if initialize_version_from_local_files:
69
- self.add_version(project=project)
59
+ def create(self, project: DCMProjectEntityModel) -> None:
60
+ query = f"CREATE DCM PROJECT {project.fqn.sql_identifier}"
61
+ self.execute_query(query)
70
62
 
71
63
  def _create_version(
72
64
  self,
@@ -84,43 +76,11 @@ class DCMProjectManager(SqlExecutionMixin):
84
76
  query += f" COMMENT = '{comment}'"
85
77
  return self.execute_query(query=query)
86
78
 
87
- def add_version(
88
- self,
89
- project: DCMProjectEntityModel,
90
- prune: bool = False,
91
- from_stage: Optional[str] = None,
92
- alias: Optional[str] = None,
93
- comment: Optional[str] = None,
94
- ):
95
- """
96
- Adds a version to DCM Project. If [from_stage] is not defined,
97
- uploads local files to the stage defined in DCM Project definition.
98
- """
99
-
100
- if not from_stage:
101
- cli_context = get_cli_context()
102
- from_stage = project.stage
103
- with cli_console.phase("Uploading artifacts"):
104
- sync_artifacts_with_stage(
105
- project_paths=ProjectPaths(project_root=cli_context.project_root),
106
- stage_root=from_stage,
107
- artifacts=project.artifacts,
108
- prune=prune,
109
- )
110
-
111
- with cli_console.phase(f"Creating DCM Project version from stage {from_stage}"):
112
- return self._create_version(
113
- project_name=project.fqn,
114
- from_stage=from_stage, # type:ignore
115
- alias=alias,
116
- comment=comment,
117
- )
118
-
119
79
  def list_versions(self, project_name: FQN):
120
80
  query = f"SHOW VERSIONS IN DCM PROJECT {project_name.identifier}"
121
81
  return self.execute_query(query=query)
122
82
 
123
- def drop_version(
83
+ def drop_deployment(
124
84
  self,
125
85
  project_name: FQN,
126
86
  version_name: str,
@@ -60,6 +60,8 @@ class NotebookEntity(EntityBase[NotebookEntityModel]):
60
60
  query += f"\nCOMPUTE_POOL = '{self.model.compute_pool}'"
61
61
  if self.model.runtime_name:
62
62
  query += f"\nRUNTIME_NAME = '{self.model.runtime_name}'"
63
+ if self.model.runtime_environment_version and not self.model.compute_pool:
64
+ query += f"\nRUNTIME_ENVIRONMENT_VERSION = '{self.model.runtime_environment_version}'"
63
65
 
64
66
  query += (
65
67
  ";\n// Cannot use IDENTIFIER(...)"
@@ -20,6 +20,9 @@ class NotebookEntityModel(EntityModelBaseWithArtifacts):
20
20
  )
21
21
  notebook_file: Path = Field(title="Notebook file")
22
22
  query_warehouse: str = Field(title="Snowflake warehouse to execute the notebook")
23
+ runtime_environment_version: Optional[str] = Field(
24
+ title="Runtime environment version", default=None
25
+ )
23
26
  compute_pool: Optional[str] = Field(
24
27
  title="Compute pool to run the notebook in", default=None
25
28
  )
@@ -37,6 +40,10 @@ class NotebookEntityModel(EntityModelBaseWithArtifacts):
37
40
  def validate_container_setup(self):
38
41
  if self.compute_pool and not self.runtime_name:
39
42
  raise ValueError("compute_pool is specified without runtime_name")
40
- if self.runtime_name and not self.compute_pool and not self:
43
+ if self.runtime_name and not self.compute_pool:
41
44
  raise ValueError("runtime_name is specified without compute_pool")
45
+ if self.compute_pool and self.runtime_environment_version:
46
+ raise ValueError(
47
+ "runtime_environment_version is only applicable for notebooks using warehouse, not compute pool"
48
+ )
42
49
  return self
@@ -22,8 +22,10 @@ from snowflake.cli._plugins.object.commands import (
22
22
  ScopeOption,
23
23
  describe,
24
24
  drop,
25
+ limit_option_,
25
26
  list_,
26
27
  scope_option, # noqa: F401
28
+ terse_option_,
27
29
  )
28
30
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
29
31
  from snowflake.cli.api.constants import ObjectType
@@ -37,6 +39,8 @@ def add_object_command_aliases(
37
39
  like_option: Optional[typer.Option],
38
40
  scope_option: Optional[typer.Option],
39
41
  ommit_commands: Optional[List[str]] = None,
42
+ terse_option: Optional[typer.Option] = None,
43
+ limit_option: Optional[typer.Option] = None,
40
44
  ):
41
45
  if ommit_commands is None:
42
46
  ommit_commands = list()
@@ -47,11 +51,18 @@ def add_object_command_aliases(
47
51
  if not scope_option:
48
52
 
49
53
  @app.command("list", requires_connection=True)
50
- def list_cmd(like: str = like_option, **options): # type: ignore
54
+ def list_cmd(
55
+ like: str = like_option, # type: ignore
56
+ terse: bool = terse_option if terse_option else terse_option_(), # type: ignore
57
+ limit: Optional[int] = limit_option if limit_option else limit_option_(), # type: ignore
58
+ **options,
59
+ ):
51
60
  return list_(
52
61
  object_type=object_type.value.cli_name,
53
62
  like=like,
54
63
  scope=ScopeOption.default,
64
+ terse=terse,
65
+ limit=limit,
55
66
  **options,
56
67
  )
57
68
 
@@ -61,12 +72,16 @@ def add_object_command_aliases(
61
72
  def list_cmd(
62
73
  like: str = like_option, # type: ignore
63
74
  scope: Tuple[str, str] = scope_option, # type: ignore
75
+ terse: bool = terse_option if terse_option else terse_option_(), # type: ignore
76
+ limit: Optional[int] = limit_option if limit_option else limit_option_(), # type: ignore
64
77
  **options,
65
78
  ):
66
79
  return list_(
67
80
  object_type=object_type.value.cli_name,
68
81
  like=like,
69
82
  scope=scope,
83
+ terse=terse,
84
+ limit=limit,
70
85
  **options,
71
86
  )
72
87
 
@@ -94,6 +94,24 @@ def scope_option(help_example: str):
94
94
  )
95
95
 
96
96
 
97
+ def terse_option_():
98
+ return typer.Option(
99
+ None,
100
+ "--terse",
101
+ help=f"Returns only a subset of available columns.",
102
+ hidden=True,
103
+ )
104
+
105
+
106
+ def limit_option_():
107
+ return typer.Option(
108
+ None,
109
+ "--limit",
110
+ help=f"Limits the maximum number of rows returned.",
111
+ hidden=True,
112
+ )
113
+
114
+
97
115
  ScopeOption = scope_option(
98
116
  help_example="`list table --in database my_db`. Some object types have specialized scopes (e.g. list service --in compute-pool my_pool)"
99
117
  )
@@ -110,11 +128,19 @@ def list_(
110
128
  object_type: str = ObjectArgument,
111
129
  like: str = LikeOption,
112
130
  scope: Tuple[str, str] = ScopeOption,
131
+ terse: Optional[bool] = terse_option_(),
132
+ limit: Optional[int] = limit_option_(),
113
133
  **options,
114
134
  ):
115
135
  _scope_validate(object_type, scope)
116
136
  return QueryResult(
117
- ObjectManager().show(object_type=object_type, like=like, scope=scope)
137
+ ObjectManager().show(
138
+ object_type=object_type,
139
+ like=like,
140
+ scope=scope,
141
+ terse=terse,
142
+ limit=limit,
143
+ )
118
144
  )
119
145
 
120
146