snowflake-cli 3.10.1__py3-none-any.whl → 3.12.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 (61) 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/cli_app.py +0 -1
  6. snowflake/cli/_app/constants.py +10 -0
  7. snowflake/cli/_app/printing.py +153 -19
  8. snowflake/cli/_app/snow_connector.py +35 -0
  9. snowflake/cli/_plugins/auth/__init__.py +4 -2
  10. snowflake/cli/_plugins/auth/keypair/commands.py +2 -0
  11. snowflake/cli/_plugins/auth/oidc/__init__.py +13 -0
  12. snowflake/cli/_plugins/auth/oidc/commands.py +47 -0
  13. snowflake/cli/_plugins/auth/oidc/manager.py +66 -0
  14. snowflake/cli/_plugins/auth/oidc/plugin_spec.py +30 -0
  15. snowflake/cli/_plugins/connection/commands.py +37 -3
  16. snowflake/cli/_plugins/dbt/commands.py +37 -8
  17. snowflake/cli/_plugins/dbt/manager.py +144 -12
  18. snowflake/cli/_plugins/dcm/commands.py +102 -136
  19. snowflake/cli/_plugins/dcm/manager.py +136 -89
  20. snowflake/cli/_plugins/logs/commands.py +7 -0
  21. snowflake/cli/_plugins/logs/manager.py +21 -1
  22. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +3 -1
  23. snowflake/cli/_plugins/notebook/notebook_entity.py +2 -0
  24. snowflake/cli/_plugins/notebook/notebook_entity_model.py +8 -1
  25. snowflake/cli/_plugins/object/command_aliases.py +16 -1
  26. snowflake/cli/_plugins/object/commands.py +27 -1
  27. snowflake/cli/_plugins/object/manager.py +12 -1
  28. snowflake/cli/_plugins/snowpark/commands.py +8 -1
  29. snowflake/cli/_plugins/snowpark/common.py +1 -0
  30. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +29 -5
  31. snowflake/cli/_plugins/snowpark/package_utils.py +44 -3
  32. snowflake/cli/_plugins/spcs/services/manager.py +5 -4
  33. snowflake/cli/_plugins/sql/lexer/types.py +1 -0
  34. snowflake/cli/_plugins/sql/repl.py +100 -26
  35. snowflake/cli/_plugins/sql/repl_commands.py +607 -0
  36. snowflake/cli/_plugins/sql/statement_reader.py +44 -20
  37. snowflake/cli/api/artifacts/bundle_map.py +32 -2
  38. snowflake/cli/api/artifacts/regex_resolver.py +54 -0
  39. snowflake/cli/api/artifacts/upload.py +5 -1
  40. snowflake/cli/api/artifacts/utils.py +12 -1
  41. snowflake/cli/api/cli_global_context.py +7 -0
  42. snowflake/cli/api/commands/decorators.py +7 -0
  43. snowflake/cli/api/commands/flags.py +26 -0
  44. snowflake/cli/api/config.py +24 -0
  45. snowflake/cli/api/connections.py +1 -0
  46. snowflake/cli/api/console/abc.py +13 -2
  47. snowflake/cli/api/console/console.py +20 -0
  48. snowflake/cli/api/constants.py +9 -0
  49. snowflake/cli/api/entities/utils.py +10 -6
  50. snowflake/cli/api/feature_flags.py +1 -0
  51. snowflake/cli/api/identifiers.py +18 -1
  52. snowflake/cli/api/project/schemas/entities/entities.py +0 -6
  53. snowflake/cli/api/rendering/sql_templates.py +2 -0
  54. snowflake/cli/api/utils/dict_utils.py +42 -1
  55. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.12.0.dist-info}/METADATA +15 -41
  56. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.12.0.dist-info}/RECORD +59 -52
  57. snowflake/cli/_plugins/dcm/dcm_project_entity_model.py +0 -59
  58. snowflake/cli/_plugins/sql/snowsql_commands.py +0 -331
  59. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.12.0.dist-info}/WHEEL +0 -0
  60. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.12.0.dist-info}/entry_points.txt +0 -0
  61. {snowflake_cli-3.10.1.dist-info → snowflake_cli-3.12.0.dist-info}/licenses/LICENSE +0 -0
@@ -11,33 +11,26 @@
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
18
- from snowflake.cli._plugins.dcm.dcm_project_entity_model import (
19
- DCMProjectEntityModel,
20
- )
21
17
  from snowflake.cli._plugins.dcm.manager import DCMProjectManager
22
18
  from snowflake.cli._plugins.object.command_aliases import add_object_command_aliases
23
19
  from snowflake.cli._plugins.object.commands import scope_option
24
20
  from snowflake.cli._plugins.object.manager import ObjectManager
25
- from snowflake.cli.api.cli_global_context import get_cli_context
26
- from snowflake.cli.api.commands.decorators import with_project_definition
27
21
  from snowflake.cli.api.commands.flags import (
28
22
  IfExistsOption,
29
23
  IfNotExistsOption,
30
24
  OverrideableOption,
31
- PruneOption,
32
- entity_argument,
33
25
  identifier_argument,
34
26
  like_option,
35
27
  variables_option,
36
28
  )
37
29
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
38
- from snowflake.cli.api.commands.utils import get_entity_for_operation
39
30
  from snowflake.cli.api.console.console import cli_console
40
- from snowflake.cli.api.constants import ObjectType
31
+ from snowflake.cli.api.constants import (
32
+ ObjectType,
33
+ )
41
34
  from snowflake.cli.api.exceptions import CliError
42
35
  from snowflake.cli.api.feature_flags import FeatureFlag
43
36
  from snowflake.cli.api.identifiers import FQN
@@ -46,6 +39,7 @@ from snowflake.cli.api.output.types import (
46
39
  QueryJsonValueResult,
47
40
  QueryResult,
48
41
  )
42
+ from snowflake.cli.api.utils.path_utils import is_stage_path
49
43
 
50
44
  app = SnowTyperFactory(
51
45
  name="dcm",
@@ -54,12 +48,6 @@ app = SnowTyperFactory(
54
48
  )
55
49
 
56
50
  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
51
  variables_flag = variables_option(
64
52
  'Variables for the execution context; for example: `-D "<key>=<value>"`.'
65
53
  )
@@ -69,9 +57,36 @@ configuration_flag = typer.Option(
69
57
  help="Configuration of the DCM Project to use. If not specified default configuration is used.",
70
58
  show_default=False,
71
59
  )
72
- from_option = OverrideableOption(
60
+ from_option = typer.Option(
73
61
  None,
74
62
  "--from",
63
+ help="Source location: stage path (starting with '@') or local directory path. Omit to use current directory.",
64
+ show_default=False,
65
+ )
66
+
67
+ alias_option = typer.Option(
68
+ None,
69
+ "--alias",
70
+ help="Alias for the deployment.",
71
+ show_default=False,
72
+ )
73
+ output_path_option = OverrideableOption(
74
+ None,
75
+ "--output-path",
76
+ show_default=False,
77
+ )
78
+
79
+ terse_option = typer.Option(
80
+ False,
81
+ "--terse",
82
+ help="Returns only a subset of output columns.",
83
+ show_default=False,
84
+ )
85
+
86
+ limit_option = typer.Option(
87
+ None,
88
+ "--limit",
89
+ help="Limits the maximum number of rows returned.",
75
90
  show_default=False,
76
91
  )
77
92
 
@@ -85,73 +100,73 @@ add_object_command_aliases(
85
100
  ),
86
101
  scope_option=scope_option(help_example="`list --in database my_db`"),
87
102
  ommit_commands=["create"],
103
+ terse_option=terse_option,
104
+ limit_option=limit_option,
88
105
  )
89
106
 
90
107
 
91
108
  @app.command(requires_connection=True)
92
109
  def deploy(
93
110
  identifier: FQN = dcm_identifier,
94
- version: Optional[str] = version_flag,
95
- from_stage: Optional[str] = from_option(
96
- help="Apply changes defined in given stage instead of using a specific project version."
97
- ),
111
+ from_location: Optional[str] = from_option,
98
112
  variables: Optional[List[str]] = variables_flag,
99
113
  configuration: Optional[str] = configuration_flag,
114
+ alias: Optional[str] = alias_option,
100
115
  **options,
101
116
  ):
102
117
  """
103
118
  Applies changes defined in DCM Project to Snowflake.
104
119
  """
105
- if version and from_stage:
106
- raise CliError("--version and --from are mutually exclusive.")
107
-
108
- result = DCMProjectManager().execute(
109
- project_name=identifier,
110
- configuration=configuration,
111
- version=version,
112
- from_stage=from_stage,
113
- variables=variables,
114
- )
120
+ manager = DCMProjectManager()
121
+ effective_stage = _get_effective_stage(identifier, from_location)
122
+
123
+ with cli_console.spinner() as spinner:
124
+ spinner.add_task(description=f"Deploying dcm project {identifier}", total=None)
125
+ result = manager.execute(
126
+ project_identifier=identifier,
127
+ configuration=configuration,
128
+ from_stage=effective_stage,
129
+ variables=variables,
130
+ alias=alias,
131
+ output_path=None,
132
+ )
115
133
  return QueryJsonValueResult(result)
116
134
 
117
135
 
118
136
  @app.command(requires_connection=True)
119
137
  def plan(
120
138
  identifier: FQN = dcm_identifier,
121
- version: Optional[str] = version_flag,
122
- from_stage: Optional[str] = from_option(
123
- help="Plan DCM Project deployment from given stage instead of using a specific version."
124
- ),
139
+ from_location: Optional[str] = from_option,
125
140
  variables: Optional[List[str]] = variables_flag,
126
141
  configuration: Optional[str] = configuration_flag,
142
+ output_path: Optional[str] = output_path_option(
143
+ help="Path where the deployment plan output will be stored. Can be a stage path (starting with '@') or a local directory path."
144
+ ),
127
145
  **options,
128
146
  ):
129
147
  """
130
148
  Plans a DCM Project deployment (validates without executing).
131
149
  """
132
- if version and from_stage:
133
- raise CliError("--version and --from are mutually exclusive.")
150
+ manager = DCMProjectManager()
151
+ effective_stage = _get_effective_stage(identifier, from_location)
152
+
153
+ with cli_console.spinner() as spinner:
154
+ spinner.add_task(description=f"Planning dcm project {identifier}", total=None)
155
+ result = manager.execute(
156
+ project_identifier=identifier,
157
+ configuration=configuration,
158
+ from_stage=effective_stage,
159
+ dry_run=True,
160
+ variables=variables,
161
+ output_path=output_path,
162
+ )
134
163
 
135
- result = DCMProjectManager().execute(
136
- project_name=identifier,
137
- configuration=configuration,
138
- version=version,
139
- from_stage=from_stage,
140
- dry_run=True,
141
- variables=variables,
142
- )
143
164
  return QueryJsonValueResult(result)
144
165
 
145
166
 
146
167
  @app.command(requires_connection=True)
147
- @with_project_definition()
148
168
  def create(
149
- 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
- ),
169
+ identifier: FQN = dcm_identifier,
155
170
  if_not_exists: bool = IfNotExistsOption(
156
171
  help="Do nothing if the project already exists."
157
172
  ),
@@ -159,124 +174,75 @@ def create(
159
174
  ):
160
175
  """
161
176
  Creates a DCM Project in Snowflake.
162
- By default, the DCM Project is initialized with a new version created from local files.
163
177
  """
164
- cli_context = get_cli_context()
165
- project: DCMProjectEntityModel = get_entity_for_operation(
166
- cli_context=cli_context,
167
- entity_id=entity_id,
168
- project_definition=cli_context.project_definition,
169
- entity_type="dcm",
170
- )
171
178
  om = ObjectManager()
172
- if om.object_exists(object_type="dcm", fqn=project.fqn):
173
- message = f"DCM Project '{project.fqn}' already exists."
179
+ if om.object_exists(object_type="dcm", fqn=identifier):
180
+ message = f"DCM Project '{identifier}' already exists."
174
181
  if if_not_exists:
175
182
  return MessageResult(message)
176
183
  raise CliError(message)
177
184
 
178
- if not no_version and om.object_exists(
179
- object_type="stage", fqn=FQN.from_stage(project.stage)
180
- ):
181
- raise CliError(f"Stage '{project.stage}' already exists.")
182
-
183
185
  dpm = DCMProjectManager()
184
- with cli_console.phase(f"Creating DCM Project '{project.fqn}'"):
185
- dpm.create(project=project, initialize_version_from_local_files=not no_version)
186
+ with cli_console.phase(f"Creating DCM Project '{identifier}'"):
187
+ dpm.create(project_identifier=identifier)
186
188
 
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
- )
189
+ return MessageResult(f"DCM Project '{identifier}' successfully created.")
192
190
 
193
191
 
194
192
  @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(
193
+ def list_deployments(
243
194
  identifier: FQN = dcm_identifier,
244
195
  **options,
245
196
  ):
246
197
  """
247
- Lists versions of given DCM Project.
198
+ Lists deployments of given DCM Project.
248
199
  """
249
200
  pm = DCMProjectManager()
250
- results = pm.list_versions(project_name=identifier)
201
+ results = pm.list_deployments(project_identifier=identifier)
251
202
  return QueryResult(results)
252
203
 
253
204
 
254
205
  @app.command(requires_connection=True)
255
- def drop_version(
206
+ def drop_deployment(
256
207
  identifier: FQN = dcm_identifier,
257
- version_name: str = typer.Argument(
258
- help="Name or alias of the version to drop. For names containing '$', use single quotes to prevent shell expansion (e.g., 'VERSION$1').",
208
+ deployment_name: str = typer.Argument(
209
+ help="Name or alias of the deployment to drop. For names containing '$', use single quotes to prevent shell expansion (e.g., 'DEPLOYMENT$1').",
259
210
  show_default=False,
260
211
  ),
261
- if_exists: bool = IfExistsOption(help="Do nothing if the version does not exist."),
212
+ if_exists: bool = IfExistsOption(
213
+ help="Do nothing if the deployment does not exist."
214
+ ),
262
215
  **options,
263
216
  ):
264
217
  """
265
- Drops a version from the DCM Project.
218
+ Drops a deployment from the DCM Project.
266
219
  """
267
220
  # Detect potential shell expansion issues
268
- if version_name and version_name.upper() == "VERSION":
221
+ if deployment_name and deployment_name.upper() == "DEPLOYMENT":
269
222
  cli_console.warning(
270
- f"Version name '{version_name}' might be truncated due to shell expansion. "
271
- f"If you meant to use a version like 'VERSION$1', try using single quotes: 'VERSION$1'."
223
+ f"Deployment name '{deployment_name}' might be truncated due to shell expansion. "
224
+ f"If you meant to use a deployment like 'DEPLOYMENT$1', try using single quotes: 'DEPLOYMENT$1'."
272
225
  )
273
226
 
274
227
  dpm = DCMProjectManager()
275
- dpm.drop_version(
276
- project_name=identifier,
277
- version_name=version_name,
228
+ dpm.drop_deployment(
229
+ project_identifier=identifier,
230
+ deployment_name=deployment_name,
278
231
  if_exists=if_exists,
279
232
  )
280
233
  return MessageResult(
281
- f"Version '{version_name}' dropped from DCM Project '{identifier}'."
234
+ f"Deployment '{deployment_name}' dropped from DCM Project '{identifier}'."
282
235
  )
236
+
237
+
238
+ def _get_effective_stage(identifier: FQN, from_location: Optional[str]):
239
+ manager = DCMProjectManager()
240
+ if not from_location:
241
+ from_stage = manager.sync_local_files(project_identifier=identifier)
242
+ elif is_stage_path(from_location):
243
+ from_stage = from_location
244
+ else:
245
+ from_stage = manager.sync_local_files(
246
+ project_identifier=identifier, source_directory=from_location
247
+ )
248
+ return from_stage
@@ -11,126 +11,173 @@
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
+ from contextlib import contextmanager, nullcontext
15
+ from pathlib import Path
16
+ from typing import Generator, List
16
17
 
17
- from snowflake.cli._plugins.dcm.dcm_project_entity_model import DCMProjectEntityModel
18
+ import yaml
18
19
  from snowflake.cli._plugins.stage.manager import StageManager
19
20
  from snowflake.cli.api.artifacts.upload import sync_artifacts_with_stage
20
- from snowflake.cli.api.cli_global_context import get_cli_context
21
21
  from snowflake.cli.api.commands.utils import parse_key_value_variables
22
22
  from snowflake.cli.api.console.console import cli_console
23
+ from snowflake.cli.api.constants import (
24
+ DEFAULT_SIZE_LIMIT_MB,
25
+ ObjectType,
26
+ PatternMatchingType,
27
+ )
28
+ from snowflake.cli.api.exceptions import CliError
23
29
  from snowflake.cli.api.identifiers import FQN
24
30
  from snowflake.cli.api.project.project_paths import ProjectPaths
31
+ from snowflake.cli.api.project.schemas.entities.common import PathMapping
32
+ from snowflake.cli.api.secure_path import SecurePath
25
33
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
26
34
  from snowflake.cli.api.stage_path import StagePath
27
- from snowflake.connector.cursor import SnowflakeCursor
35
+ from snowflake.cli.api.utils.path_utils import is_stage_path
36
+
37
+ MANIFEST_FILE_NAME = "manifest.yml"
38
+ DCM_PROJECT_TYPE = "dcm_project"
28
39
 
29
40
 
30
41
  class DCMProjectManager(SqlExecutionMixin):
42
+ @contextmanager
43
+ def _collect_output(
44
+ self, project_identifier: FQN, output_path: str
45
+ ) -> Generator[str, None, None]:
46
+ """
47
+ Context manager for handling output path - creates temporary stage for local paths,
48
+ downloads files after execution, and ensures proper cleanup.
49
+
50
+ Args:
51
+ project_identifier: The DCM project identifier
52
+ output_path: Either a stage path (@stage/path) or local directory path
53
+
54
+ Yields:
55
+ str: The effective output path to use in the DCM command
56
+ """
57
+ temp_stage_for_local_output = None
58
+ stage_manager = StageManager()
59
+
60
+ if should_download_files := not is_stage_path(output_path):
61
+ temp_stage_fqn = FQN.from_resource(
62
+ ObjectType.DCM_PROJECT, project_identifier, "OUTPUT_TMP_STAGE"
63
+ )
64
+ stage_manager.create(temp_stage_fqn, temporary=True)
65
+ effective_output_path = StagePath.from_stage_str(temp_stage_fqn.identifier)
66
+ temp_stage_for_local_output = (temp_stage_fqn.identifier, Path(output_path))
67
+ else:
68
+ effective_output_path = StagePath.from_stage_str(output_path)
69
+
70
+ yield effective_output_path.absolute_path()
71
+
72
+ if should_download_files:
73
+ assert temp_stage_for_local_output is not None
74
+ stage_path, local_path = temp_stage_for_local_output
75
+ stage_manager.get_recursive(stage_path=stage_path, dest_path=local_path)
76
+ cli_console.step(f"Plan output saved to: {local_path.resolve()}")
77
+ else:
78
+ cli_console.step(f"Plan output saved to: {output_path}")
79
+
31
80
  def execute(
32
81
  self,
33
- project_name: FQN,
82
+ project_identifier: FQN,
83
+ from_stage: str,
34
84
  configuration: str | None = None,
35
- version: str | None = None,
36
- from_stage: str | None = None,
37
85
  variables: List[str] | None = None,
38
86
  dry_run: bool = False,
87
+ alias: str | None = None,
88
+ output_path: str | None = None,
39
89
  ):
40
- query = f"EXECUTE DCM PROJECT {project_name.sql_identifier}"
41
- if dry_run:
42
- query += " PLAN"
43
- else:
44
- query += " DEPLOY"
45
- if configuration or variables:
46
- query += f" USING"
47
- if configuration:
48
- query += f" CONFIGURATION {configuration}"
49
- if variables:
50
- query += StageManager.parse_execute_variables(
51
- parse_key_value_variables(variables)
52
- ).removeprefix(" using")
53
- if version:
54
- query += f" WITH VERSION {version}"
55
- elif from_stage:
90
+ with self._collect_output(project_identifier, output_path) if (
91
+ output_path and dry_run
92
+ ) else nullcontext() as output_stage:
93
+ query = f"EXECUTE DCM PROJECT {project_identifier.sql_identifier}"
94
+ if dry_run:
95
+ query += " PLAN"
96
+ else:
97
+ query += " DEPLOY"
98
+ if alias:
99
+ query += f' AS "{alias}"'
100
+ if configuration or variables:
101
+ query += f" USING"
102
+ if configuration:
103
+ query += f" CONFIGURATION {configuration}"
104
+ if variables:
105
+ query += StageManager.parse_execute_variables(
106
+ parse_key_value_variables(variables)
107
+ ).removeprefix(" using")
56
108
  stage_path = StagePath.from_stage_str(from_stage)
57
109
  query += f" FROM {stage_path.absolute_path()}"
58
- return self.execute_query(query=query)
110
+ if output_stage is not None:
111
+ query += f" OUTPUT_PATH {output_stage}"
112
+ result = self.execute_query(query=query)
59
113
 
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)
114
+ return result
63
115
 
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)
116
+ def create(self, project_identifier: FQN) -> None:
117
+ query = f"CREATE DCM PROJECT {project_identifier.sql_identifier}"
118
+ self.execute_query(query)
70
119
 
71
- def _create_version(
72
- self,
73
- project_name: FQN,
74
- from_stage: str,
75
- alias: str | None = None,
76
- comment: str | None = None,
77
- ):
78
- stage_path = StagePath.from_stage_str(from_stage)
79
- query = f"ALTER DCM PROJECT {project_name.identifier} ADD VERSION"
80
- if alias:
81
- query += f" IF NOT EXISTS {alias}"
82
- query += f" FROM {stage_path.absolute_path(at_prefix=True)}"
83
- if comment:
84
- query += f" COMMENT = '{comment}'"
120
+ def list_deployments(self, project_identifier: FQN):
121
+ query = f"SHOW DEPLOYMENTS IN DCM PROJECT {project_identifier.identifier}"
85
122
  return self.execute_query(query=query)
86
123
 
87
- def add_version(
124
+ def drop_deployment(
88
125
  self,
89
- project: DCMProjectEntityModel,
90
- prune: bool = False,
91
- from_stage: Optional[str] = None,
92
- alias: Optional[str] = None,
93
- comment: Optional[str] = None,
126
+ project_identifier: FQN,
127
+ deployment_name: str,
128
+ if_exists: bool = False,
94
129
  ):
95
130
  """
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.
131
+ Drops a deployment from the DCM Project.
98
132
  """
133
+ query = f"ALTER DCM PROJECT {project_identifier.identifier} DROP DEPLOYMENT"
134
+ if if_exists:
135
+ query += " IF EXISTS"
136
+ query += f' "{deployment_name}"'
137
+ return self.execute_query(query=query)
99
138
 
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
- )
139
+ @staticmethod
140
+ def sync_local_files(
141
+ project_identifier: FQN, source_directory: str | None = None
142
+ ) -> str:
143
+ source_path = (
144
+ SecurePath(source_directory).resolve()
145
+ if source_directory
146
+ else SecurePath.cwd()
147
+ )
110
148
 
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,
149
+ dcm_manifest_file = source_path / MANIFEST_FILE_NAME
150
+ if not dcm_manifest_file.exists():
151
+ raise CliError(
152
+ f"{MANIFEST_FILE_NAME} was not found in directory {source_path.path}"
117
153
  )
118
154
 
119
- def list_versions(self, project_name: FQN):
120
- query = f"SHOW VERSIONS IN DCM PROJECT {project_name.identifier}"
121
- return self.execute_query(query=query)
155
+ with dcm_manifest_file.open(read_file_limit_mb=DEFAULT_SIZE_LIMIT_MB) as fd:
156
+ dcm_manifest = yaml.safe_load(fd)
157
+ object_type = dcm_manifest.get("type") if dcm_manifest else None
158
+ if object_type is None:
159
+ raise CliError(
160
+ f"Manifest file type is undefined. Expected {DCM_PROJECT_TYPE}"
161
+ )
162
+ if object_type.lower() != DCM_PROJECT_TYPE:
163
+ raise CliError(
164
+ f"Manifest file is defined for type {object_type}. Expected {DCM_PROJECT_TYPE}"
165
+ )
122
166
 
123
- def drop_version(
124
- self,
125
- project_name: FQN,
126
- version_name: str,
127
- if_exists: bool = False,
128
- ):
129
- """
130
- Drops a version from the DCM Project.
131
- """
132
- query = f"ALTER DCM PROJECT {project_name.identifier} DROP VERSION"
133
- if if_exists:
134
- query += " IF EXISTS"
135
- query += f" {version_name}"
136
- return self.execute_query(query=query)
167
+ definitions = list(dcm_manifest.get("include_definitions", list()))
168
+ if MANIFEST_FILE_NAME not in definitions:
169
+ definitions.append(MANIFEST_FILE_NAME)
170
+
171
+ with cli_console.phase(f"Uploading definition files"):
172
+ stage_fqn = FQN.from_resource(
173
+ ObjectType.DCM_PROJECT, project_identifier, "TMP_STAGE"
174
+ )
175
+ sync_artifacts_with_stage(
176
+ project_paths=ProjectPaths(project_root=source_path.path),
177
+ stage_root=stage_fqn.identifier,
178
+ use_temporary_stage=True,
179
+ artifacts=[PathMapping(src=definition) for definition in definitions],
180
+ pattern_type=PatternMatchingType.REGEX,
181
+ )
182
+
183
+ return stage_fqn.identifier
@@ -48,6 +48,11 @@ def get_logs(
48
48
  "--log-level",
49
49
  help="The log level to filter by. If not provided, INFO will be used",
50
50
  ),
51
+ partial_match: bool = typer.Option(
52
+ False,
53
+ "--partial",
54
+ help="Enable partial, case-insensitive matching for object names",
55
+ ),
51
56
  **options,
52
57
  ):
53
58
  """
@@ -75,6 +80,7 @@ def get_logs(
75
80
  refresh_time=refresh_time,
76
81
  event_table=event_table,
77
82
  log_level=log_level,
83
+ partial_match=partial_match,
78
84
  )
79
85
  logs = itertools.chain(
80
86
  (MessageResult(log.log_message) for logs in logs_stream for log in logs)
@@ -87,6 +93,7 @@ def get_logs(
87
93
  to_time=to_time,
88
94
  event_table=event_table,
89
95
  log_level=log_level,
96
+ partial_match=partial_match,
90
97
  )
91
98
  logs = (MessageResult(log.log_message) for log in logs_iterable) # type: ignore
92
99