snowflake-cli 3.3.0__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.
Files changed (112) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/__main__.py +2 -2
  3. snowflake/cli/_app/cli_app.py +220 -197
  4. snowflake/cli/_app/commands_registration/builtin_plugins.py +5 -1
  5. snowflake/cli/_app/commands_registration/command_plugins_loader.py +3 -1
  6. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +4 -30
  7. snowflake/cli/_app/printing.py +2 -2
  8. snowflake/cli/_plugins/connection/commands.py +2 -4
  9. snowflake/cli/_plugins/cortex/commands.py +2 -4
  10. snowflake/cli/_plugins/git/manager.py +1 -1
  11. snowflake/cli/_plugins/helpers/commands.py +3 -4
  12. snowflake/cli/_plugins/nativeapp/artifacts.py +6 -624
  13. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  14. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -3
  16. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  17. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -2
  18. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +2 -2
  19. snowflake/cli/_plugins/nativeapp/commands.py +21 -19
  20. snowflake/cli/_plugins/nativeapp/entities/application.py +16 -19
  21. snowflake/cli/_plugins/nativeapp/entities/application_package.py +142 -55
  22. snowflake/cli/_plugins/nativeapp/release_channel/commands.py +37 -3
  23. snowflake/cli/_plugins/nativeapp/release_directive/commands.py +80 -2
  24. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +224 -44
  25. snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +2 -2
  26. snowflake/cli/_plugins/nativeapp/version/commands.py +1 -1
  27. snowflake/cli/_plugins/notebook/commands.py +54 -2
  28. snowflake/cli/_plugins/notebook/exceptions.py +1 -1
  29. snowflake/cli/_plugins/notebook/manager.py +3 -3
  30. snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
  31. snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
  32. snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
  33. snowflake/cli/_plugins/notebook/types.py +3 -0
  34. snowflake/cli/_plugins/plugin/commands.py +79 -0
  35. snowflake/cli/_plugins/plugin/manager.py +74 -0
  36. snowflake/cli/_plugins/plugin/plugin_spec.py +30 -0
  37. snowflake/cli/_plugins/project/__init__.py +0 -0
  38. snowflake/cli/_plugins/project/commands.py +157 -0
  39. snowflake/cli/_plugins/project/feature_flags.py +22 -0
  40. snowflake/cli/_plugins/project/manager.py +76 -0
  41. snowflake/cli/_plugins/project/plugin_spec.py +30 -0
  42. snowflake/cli/_plugins/project/project_entity_model.py +40 -0
  43. snowflake/cli/_plugins/snowpark/commands.py +49 -30
  44. snowflake/cli/_plugins/snowpark/common.py +47 -2
  45. snowflake/cli/_plugins/snowpark/snowpark_entity.py +38 -25
  46. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
  47. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
  48. snowflake/cli/_plugins/snowpark/zipper.py +33 -1
  49. snowflake/cli/_plugins/spcs/compute_pool/commands.py +53 -5
  50. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity.py +8 -0
  51. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity_model.py +37 -0
  52. snowflake/cli/_plugins/spcs/compute_pool/manager.py +45 -0
  53. snowflake/cli/_plugins/spcs/image_repository/commands.py +29 -0
  54. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity.py +8 -0
  55. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity_model.py +8 -0
  56. snowflake/cli/_plugins/spcs/image_repository/manager.py +1 -1
  57. snowflake/cli/_plugins/spcs/services/commands.py +51 -1
  58. snowflake/cli/_plugins/spcs/services/manager.py +114 -0
  59. snowflake/cli/_plugins/spcs/services/service_entity.py +6 -0
  60. snowflake/cli/_plugins/spcs/services/service_entity_model.py +45 -0
  61. snowflake/cli/_plugins/spcs/services/service_project_paths.py +15 -0
  62. snowflake/cli/_plugins/stage/commands.py +2 -1
  63. snowflake/cli/_plugins/stage/diff.py +60 -39
  64. snowflake/cli/_plugins/stage/manager.py +26 -13
  65. snowflake/cli/_plugins/stage/utils.py +1 -1
  66. snowflake/cli/_plugins/streamlit/commands.py +18 -24
  67. snowflake/cli/_plugins/streamlit/manager.py +37 -27
  68. snowflake/cli/_plugins/streamlit/streamlit_entity.py +20 -41
  69. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
  70. snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
  71. snowflake/cli/_plugins/workspace/commands.py +3 -3
  72. snowflake/cli/_plugins/workspace/manager.py +1 -1
  73. snowflake/cli/api/artifacts/bundle_map.py +500 -0
  74. snowflake/cli/api/artifacts/common.py +78 -0
  75. snowflake/cli/api/artifacts/upload.py +51 -0
  76. snowflake/cli/api/artifacts/utils.py +82 -0
  77. snowflake/cli/api/cli_global_context.py +14 -1
  78. snowflake/cli/api/commands/flags.py +34 -13
  79. snowflake/cli/api/commands/snow_typer.py +12 -0
  80. snowflake/cli/api/commands/utils.py +30 -2
  81. snowflake/cli/api/config.py +15 -10
  82. snowflake/cli/api/constants.py +1 -0
  83. snowflake/cli/api/entities/common.py +14 -32
  84. snowflake/cli/api/entities/resolver.py +160 -0
  85. snowflake/cli/api/entities/utils.py +56 -15
  86. snowflake/cli/api/errno.py +3 -0
  87. snowflake/cli/api/exceptions.py +8 -1
  88. snowflake/cli/api/feature_flags.py +1 -1
  89. snowflake/cli/api/plugins/plugin_config.py +43 -4
  90. snowflake/cli/api/project/definition_conversion.py +3 -2
  91. snowflake/cli/api/project/definition_helper.py +31 -0
  92. snowflake/cli/api/project/project_paths.py +28 -0
  93. snowflake/cli/api/project/schemas/entities/common.py +130 -1
  94. snowflake/cli/api/project/schemas/entities/entities.py +30 -0
  95. snowflake/cli/api/project/schemas/project_definition.py +27 -0
  96. snowflake/cli/api/project/schemas/updatable_model.py +2 -2
  97. snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
  98. snowflake/cli/api/secure_path.py +6 -0
  99. snowflake/cli/api/sql_execution.py +5 -1
  100. snowflake/cli/api/stage_path.py +7 -2
  101. snowflake/cli/api/utils/graph.py +3 -0
  102. snowflake/cli/api/utils/path_utils.py +24 -0
  103. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/METADATA +12 -13
  104. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/RECORD +109 -85
  105. snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
  106. snowflake/cli/api/__init__.py +0 -48
  107. snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
  108. /snowflake/cli/{_app/api_impl → _plugins/plugin}/__init__.py +0 -0
  109. /snowflake/cli/{_app/api_impl/plugin → api/artifacts}/__init__.py +0 -0
  110. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/WHEEL +0 -0
  111. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/entry_points.txt +0 -0
  112. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,6 +4,7 @@ import json
4
4
  import os
5
5
  import re
6
6
  from datetime import datetime
7
+ from functools import cached_property
7
8
  from pathlib import Path
8
9
  from textwrap import dedent
9
10
  from typing import Any, List, Literal, Optional, Set, Union
@@ -13,7 +14,6 @@ from click import BadOptionUsage, ClickException, UsageError
13
14
  from pydantic import Field, field_validator
14
15
  from snowflake.cli._plugins.connection.util import UIParameter
15
16
  from snowflake.cli._plugins.nativeapp.artifacts import (
16
- BundleMap,
17
17
  VersionInfo,
18
18
  build_bundle,
19
19
  find_setup_script_file,
@@ -61,29 +61,35 @@ from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
61
61
  FunctionEntityModel,
62
62
  ProcedureEntityModel,
63
63
  )
64
- from snowflake.cli._plugins.stage.diff import DiffResult
65
- from snowflake.cli._plugins.stage.manager import StageManager
64
+ from snowflake.cli._plugins.stage.diff import DiffResult, compute_stage_diff
65
+ from snowflake.cli._plugins.stage.manager import (
66
+ DefaultStagePathParts,
67
+ StageManager,
68
+ StagePathParts,
69
+ )
70
+ from snowflake.cli._plugins.stage.utils import print_diff_to_console
66
71
  from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
67
72
  StreamlitEntityModel,
68
73
  )
69
74
  from snowflake.cli._plugins.workspace.context import ActionContext
75
+ from snowflake.cli.api.artifacts.bundle_map import BundleMap
70
76
  from snowflake.cli.api.cli_global_context import span
71
77
  from snowflake.cli.api.entities.common import (
72
78
  EntityBase,
73
79
  attach_spans_to_entity_actions,
74
- get_sql_executor,
75
80
  )
76
81
  from snowflake.cli.api.entities.utils import (
77
82
  drop_generic_object,
78
83
  execute_post_deploy_hooks,
79
84
  generic_sql_error_handler,
85
+ get_sql_executor,
80
86
  sync_deploy_root_with_stage,
81
87
  validation_item_to_str,
82
88
  )
83
89
  from snowflake.cli.api.errno import DOES_NOT_EXIST_OR_NOT_AUTHORIZED
84
90
  from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
85
91
  from snowflake.cli.api.project.schemas.entities.common import (
86
- EntityModelBase,
92
+ EntityModelBaseWithArtifacts,
87
93
  Identifier,
88
94
  PostDeployHook,
89
95
  )
@@ -93,12 +99,10 @@ from snowflake.cli.api.project.schemas.updatable_model import (
93
99
  UpdatableModel,
94
100
  )
95
101
  from snowflake.cli.api.project.schemas.v1.native_app.package import DistributionOptions
96
- from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
97
102
  from snowflake.cli.api.project.util import (
98
103
  SCHEMA_AND_NAME,
99
104
  VALID_IDENTIFIER_REGEX,
100
105
  append_test_resource_suffix,
101
- extract_schema,
102
106
  identifier_to_show_like_pattern,
103
107
  same_identifiers,
104
108
  sql_match,
@@ -147,19 +151,12 @@ class ApplicationPackageChildField(UpdatableModel):
147
151
  )
148
152
 
149
153
 
150
- class ApplicationPackageEntityModel(EntityModelBase):
154
+ class ApplicationPackageEntityModel(EntityModelBaseWithArtifacts):
151
155
  type: Literal["application package"] = DiscriminatorField() # noqa: A003
152
- artifacts: List[Union[PathMapping, str]] = Field(
153
- title="List of paths or file source/destination pairs to add to the deploy root",
154
- )
155
156
  bundle_root: Optional[str] = Field(
156
157
  title="Folder at the root of your project where artifacts necessary to perform the bundle step are stored",
157
158
  default="output/bundle/",
158
159
  )
159
- deploy_root: Optional[str] = Field(
160
- title="Folder at the root of your project where the build step copies the artifacts",
161
- default="output/deploy/",
162
- )
163
160
  children_artifacts_dir: Optional[str] = Field(
164
161
  title="Folder under deploy_root where the child artifacts will be stored",
165
162
  default="_children/",
@@ -184,6 +181,11 @@ class ApplicationPackageEntityModel(EntityModelBase):
184
181
  title="Path to manifest.yml. Unused and deprecated starting with Snowflake CLI 3.2",
185
182
  default="",
186
183
  )
184
+
185
+ stage_subdirectory: Optional[str] = Field(
186
+ title="Subfolder in stage to upload the artifacts to, instead of the root of the application package's stage",
187
+ default="",
188
+ )
187
189
  children: Optional[List[ApplicationPackageChildField]] = Field(
188
190
  title="Entities that will be bundled and deployed as part of this application package",
189
191
  default=[],
@@ -211,23 +213,6 @@ class ApplicationPackageEntityModel(EntityModelBase):
211
213
  return input_value.model_copy(update=dict(name=with_suffix))
212
214
  return with_suffix
213
215
 
214
- @field_validator("artifacts")
215
- @classmethod
216
- def transform_artifacts(
217
- cls, orig_artifacts: List[Union[PathMapping, str]]
218
- ) -> List[PathMapping]:
219
- transformed_artifacts = []
220
- if orig_artifacts is None:
221
- return transformed_artifacts
222
-
223
- for artifact in orig_artifacts:
224
- if isinstance(artifact, PathMapping):
225
- transformed_artifacts.append(artifact)
226
- else:
227
- transformed_artifacts.append(PathMapping(src=artifact))
228
-
229
- return transformed_artifacts
230
-
231
216
  @field_validator("stage")
232
217
  @classmethod
233
218
  def validate_source_stage(cls, input_value: str):
@@ -250,7 +235,11 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
250
235
 
251
236
  @property
252
237
  def deploy_root(self) -> Path:
253
- return self.project_root / self._entity_model.deploy_root
238
+ return (
239
+ self.project_root
240
+ / self._entity_model.deploy_root
241
+ / self._entity_model.stage_subdirectory
242
+ )
254
243
 
255
244
  @property
256
245
  def children_artifacts_deploy_root(self) -> Path:
@@ -281,12 +270,16 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
281
270
  ) or to_identifier(self._workspace_ctx.default_warehouse)
282
271
 
283
272
  @property
284
- def stage_fqn(self) -> str:
285
- return f"{self.name}.{self._entity_model.stage}"
273
+ def scratch_stage_path(self) -> DefaultStagePathParts:
274
+ return DefaultStagePathParts.from_fqn(
275
+ f"{self.name}.{self._entity_model.scratch_stage}"
276
+ )
286
277
 
287
- @property
288
- def scratch_stage_fqn(self) -> str:
289
- return f"{self.name}.{self._entity_model.scratch_stage}"
278
+ @cached_property
279
+ def stage_path(self) -> DefaultStagePathParts:
280
+ stage_fqn = f"{self.name}.{self._entity_model.stage}"
281
+ subdir = self._entity_model.stage_subdirectory
282
+ return DefaultStagePathParts.from_fqn(stage_fqn, subdir)
290
283
 
291
284
  @property
292
285
  def post_deploy_hooks(self) -> list[PostDeployHook] | None:
@@ -296,6 +289,23 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
296
289
  def action_bundle(self, action_ctx: ActionContext, *args, **kwargs):
297
290
  return self._bundle(action_ctx)
298
291
 
292
+ def action_diff(
293
+ self, action_ctx: ActionContext, print_to_console: bool, *args, **kwargs
294
+ ):
295
+ """
296
+ Compute the diff between the local artifacts and the remote ones on the stage.
297
+ """
298
+ bundle_map = self._bundle()
299
+ diff = compute_stage_diff(
300
+ local_root=self.deploy_root,
301
+ stage_path=self.stage_path,
302
+ )
303
+
304
+ if print_to_console:
305
+ print_diff_to_console(diff, bundle_map)
306
+
307
+ return diff
308
+
299
309
  def action_deploy(
300
310
  self,
301
311
  action_ctx: ActionContext,
@@ -305,7 +315,6 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
305
315
  validate: bool,
306
316
  interactive: bool,
307
317
  force: bool,
308
- stage_fqn: Optional[str] = None,
309
318
  *args,
310
319
  **kwargs,
311
320
  ):
@@ -317,7 +326,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
317
326
  paths=paths,
318
327
  print_diff=True,
319
328
  validate=validate,
320
- stage_fqn=stage_fqn or self.stage_fqn,
329
+ stage_path=self.stage_path,
321
330
  interactive=interactive,
322
331
  force=force,
323
332
  )
@@ -477,7 +486,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
477
486
  paths=[],
478
487
  print_diff=True,
479
488
  validate=True,
480
- stage_fqn=self.stage_fqn,
489
+ stage_path=self.stage_path,
481
490
  interactive=interactive,
482
491
  force=force,
483
492
  )
@@ -767,6 +776,58 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
767
776
  role=self.role,
768
777
  )
769
778
 
779
+ def action_release_directive_add_accounts(
780
+ self,
781
+ action_ctx: ActionContext,
782
+ release_directive: str,
783
+ release_channel: str,
784
+ target_accounts: list[str],
785
+ *args,
786
+ **kwargs,
787
+ ):
788
+ """
789
+ Adds target accounts to a release directive.
790
+ """
791
+
792
+ if not target_accounts:
793
+ raise ClickException("No target accounts provided.")
794
+
795
+ self._validate_target_accounts(target_accounts)
796
+
797
+ get_snowflake_facade().add_accounts_to_release_directive(
798
+ package_name=self.name,
799
+ release_directive=release_directive,
800
+ release_channel=self.get_sanitized_release_channel(release_channel),
801
+ target_accounts=target_accounts,
802
+ role=self.role,
803
+ )
804
+
805
+ def action_release_directive_remove_accounts(
806
+ self,
807
+ action_ctx: ActionContext,
808
+ release_directive: str,
809
+ release_channel: str,
810
+ target_accounts: list[str],
811
+ *args,
812
+ **kwargs,
813
+ ):
814
+ """
815
+ Removes target accounts from a release directive.
816
+ """
817
+
818
+ if not target_accounts:
819
+ raise ClickException("No target accounts provided.")
820
+
821
+ self._validate_target_accounts(target_accounts)
822
+
823
+ get_snowflake_facade().remove_accounts_from_release_directive(
824
+ package_name=self.name,
825
+ release_directive=release_directive,
826
+ release_channel=self.get_sanitized_release_channel(release_channel),
827
+ target_accounts=target_accounts,
828
+ role=self.role,
829
+ )
830
+
770
831
  def _print_channel_to_console(self, channel: ReleaseChannel) -> None:
771
832
  """
772
833
  Prints the release channel details to the console.
@@ -911,6 +972,31 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
911
972
  role=self.role,
912
973
  )
913
974
 
975
+ def action_release_channel_set_accounts(
976
+ self,
977
+ action_ctx: ActionContext,
978
+ release_channel: str,
979
+ target_accounts: list[str],
980
+ *args,
981
+ **kwargs,
982
+ ):
983
+ """
984
+ Sets target accounts for a release channel.
985
+ """
986
+
987
+ if not target_accounts:
988
+ raise ClickException("No target accounts provided.")
989
+
990
+ self.validate_release_channel(release_channel)
991
+ self._validate_target_accounts(target_accounts)
992
+
993
+ get_snowflake_facade().set_accounts_for_release_channel(
994
+ package_name=self.name,
995
+ release_channel=release_channel,
996
+ target_accounts=target_accounts,
997
+ role=self.role,
998
+ )
999
+
914
1000
  def action_release_channel_add_version(
915
1001
  self,
916
1002
  action_ctx: ActionContext,
@@ -1202,7 +1288,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1202
1288
  paths: list[Path],
1203
1289
  print_diff: bool,
1204
1290
  validate: bool,
1205
- stage_fqn: str,
1291
+ stage_path: StagePathParts,
1206
1292
  interactive: bool,
1207
1293
  force: bool,
1208
1294
  run_post_deploy_hooks: bool = True,
@@ -1217,7 +1303,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1217
1303
  policy = DenyAlwaysPolicy()
1218
1304
 
1219
1305
  console = workspace_ctx.console
1220
- stage_fqn = stage_fqn or self.stage_fqn
1306
+ stage_path = stage_path or self.stage_path
1221
1307
 
1222
1308
  # 1. Create a bundle if one wasn't passed in
1223
1309
  bundle_map = bundle_map or self._bundle(action_ctx)
@@ -1232,17 +1318,15 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1232
1318
 
1233
1319
  with get_sql_executor().use_role(self.role):
1234
1320
  # 3. Upload files from deploy root local folder to the above stage
1235
- stage_schema = extract_schema(stage_fqn)
1236
1321
  diff = sync_deploy_root_with_stage(
1237
1322
  console=console,
1238
1323
  deploy_root=self.deploy_root,
1239
1324
  package_name=self.name,
1240
- stage_schema=stage_schema,
1241
1325
  bundle_map=bundle_map,
1242
1326
  role=self.role,
1243
1327
  prune=prune,
1244
1328
  recursive=recursive,
1245
- stage_fqn=stage_fqn,
1329
+ stage_path=stage_path,
1246
1330
  local_paths_to_sync=paths,
1247
1331
  print_diff=print_diff,
1248
1332
  )
@@ -1321,7 +1405,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1321
1405
  get_snowflake_facade().create_version_in_package(
1322
1406
  role=self.role,
1323
1407
  package_name=self.name,
1324
- stage_fqn=self.stage_fqn,
1408
+ path_to_version_directory=self.stage_path.full_path,
1325
1409
  version=version,
1326
1410
  label=label,
1327
1411
  )
@@ -1346,7 +1430,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1346
1430
  new_patch = get_snowflake_facade().add_patch_to_package_version(
1347
1431
  role=self.role,
1348
1432
  package_name=self.name,
1349
- stage_fqn=self.stage_fqn,
1433
+ path_to_version_directory=self.stage_path.full_path,
1350
1434
  version=version,
1351
1435
  patch=patch,
1352
1436
  label=label,
@@ -1440,7 +1524,7 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1440
1524
  ) -> bool:
1441
1525
  """
1442
1526
  Returns true if the 'distribution' attribute of an existing application package in snowflake
1443
- is the same as the the attribute specified in project definition file.
1527
+ is the same as the attribute specified in project definition file.
1444
1528
  """
1445
1529
  model = self._entity_model
1446
1530
  workspace_ctx = self._workspace_ctx
@@ -1583,9 +1667,9 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1583
1667
  force: bool,
1584
1668
  ):
1585
1669
  """Call system$validate_native_app_setup() to validate deployed Native App setup script."""
1586
- stage_fqn = self.stage_fqn
1670
+ stage_path = self.stage_path
1587
1671
  if use_scratch_stage:
1588
- stage_fqn = self.scratch_stage_fqn
1672
+ stage_path = self.scratch_stage_path
1589
1673
  self._deploy(
1590
1674
  action_ctx=action_ctx,
1591
1675
  bundle_map=None,
@@ -1594,12 +1678,15 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1594
1678
  paths=[],
1595
1679
  print_diff=False,
1596
1680
  validate=False,
1597
- stage_fqn=self.scratch_stage_fqn,
1681
+ stage_path=stage_path,
1598
1682
  interactive=interactive,
1599
1683
  force=force,
1600
1684
  run_post_deploy_hooks=False,
1601
1685
  )
1602
- prefixed_stage_fqn = StageManager.get_standard_stage_prefix(stage_fqn)
1686
+ prefixed_stage_fqn = StageManager.get_standard_stage_prefix(
1687
+ stage_path.full_path
1688
+ )
1689
+
1603
1690
  sql_executor = get_sql_executor()
1604
1691
  try:
1605
1692
  cursor = sql_executor.execute_query(
@@ -1616,11 +1703,11 @@ class ApplicationPackageEntity(EntityBase[ApplicationPackageEntityModel]):
1616
1703
  finally:
1617
1704
  if use_scratch_stage:
1618
1705
  self._workspace_ctx.console.step(
1619
- f"Dropping stage {self.scratch_stage_fqn}."
1706
+ f"Dropping stage {self.scratch_stage_path.stage}."
1620
1707
  )
1621
1708
  with sql_executor.use_role(self.role):
1622
1709
  sql_executor.execute_query(
1623
- f"drop stage if exists {self.scratch_stage_fqn}"
1710
+ f"drop stage if exists {self.scratch_stage_path.stage}"
1624
1711
  )
1625
1712
 
1626
1713
  def resolve_version_info(
@@ -25,7 +25,7 @@ from snowflake.cli._plugins.workspace.manager import WorkspaceManager
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.snow_typer import SnowTyperFactory
28
- from snowflake.cli.api.entities.common import EntityActions
28
+ from snowflake.cli.api.entities.utils import EntityActions
29
29
  from snowflake.cli.api.output.formats import OutputFormat
30
30
  from snowflake.cli.api.output.types import (
31
31
  CollectionResult,
@@ -82,7 +82,7 @@ def release_channel_add_accounts(
82
82
  ),
83
83
  target_accounts: str = typer.Option(
84
84
  show_default=False,
85
- help="The accounts to add to the release channel. Format has to be `org1.account1,org2.account2`.",
85
+ help="The accounts to add to the release channel. Format must be `org1.account1,org2.account2`.",
86
86
  ),
87
87
  **options,
88
88
  ) -> CommandResult:
@@ -116,7 +116,7 @@ def release_channel_remove_accounts(
116
116
  ),
117
117
  target_accounts: str = typer.Option(
118
118
  show_default=False,
119
- help="The accounts to remove from the release channel. Format has to be `org1.account1,org2.account2`.",
119
+ help="The accounts to remove from the release channel. Format must be `org1.account1,org2.account2`.",
120
120
  ),
121
121
  **options,
122
122
  ) -> CommandResult:
@@ -140,6 +140,40 @@ def release_channel_remove_accounts(
140
140
  return MessageResult("Successfully removed accounts from the release channel.")
141
141
 
142
142
 
143
+ @with_project_definition()
144
+ @app.command("set-accounts", requires_connection=True)
145
+ @force_project_definition_v2()
146
+ def release_channel_set_accounts(
147
+ channel: str = typer.Argument(
148
+ show_default=False,
149
+ help="The release channel to set accounts for.",
150
+ ),
151
+ target_accounts: str = typer.Option(
152
+ show_default=False,
153
+ help="The accounts to set for the release channel. Format must be `org1.account1,org2.account2`.",
154
+ ),
155
+ **options,
156
+ ) -> CommandResult:
157
+ """
158
+ Sets accounts for a release channel.
159
+ """
160
+
161
+ cli_context = get_cli_context()
162
+ ws = WorkspaceManager(
163
+ project_definition=cli_context.project_definition,
164
+ project_root=cli_context.project_root,
165
+ )
166
+ package_id = options["package_entity_id"]
167
+ ws.perform_action(
168
+ package_id,
169
+ EntityActions.RELEASE_CHANNEL_SET_ACCOUNTS,
170
+ release_channel=channel,
171
+ target_accounts=target_accounts.split(","),
172
+ )
173
+
174
+ return MessageResult("Successfully set accounts for the release channel.")
175
+
176
+
143
177
  @app.command("add-version", requires_connection=True)
144
178
  @with_project_definition()
145
179
  @force_project_definition_v2()
@@ -27,7 +27,7 @@ from snowflake.cli.api.cli_global_context import get_cli_context
27
27
  from snowflake.cli.api.commands.decorators import with_project_definition
28
28
  from snowflake.cli.api.commands.flags import like_option
29
29
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
30
- from snowflake.cli.api.entities.common import EntityActions
30
+ from snowflake.cli.api.entities.utils import EntityActions
31
31
  from snowflake.cli.api.output.types import (
32
32
  CollectionResult,
33
33
  CommandResult,
@@ -95,7 +95,7 @@ def release_directive_set(
95
95
  target_accounts: Optional[str] = typer.Option(
96
96
  None,
97
97
  show_default=False,
98
- help="List of the accounts to apply the release directive to. Format has to be `org1.account1,org2.account2`",
98
+ help="List of the accounts to apply the release directive to. Format must be `org1.account1,org2.account2`",
99
99
  ),
100
100
  version: str = typer.Option(
101
101
  show_default=False,
@@ -163,3 +163,81 @@ def release_directive_unset(
163
163
  release_channel=channel,
164
164
  )
165
165
  return MessageResult(f"Successfully unset release directive {directive}.")
166
+
167
+
168
+ @app.command("add-accounts", requires_connection=True)
169
+ @with_project_definition()
170
+ @force_project_definition_v2()
171
+ def release_directive_add_accounts(
172
+ directive: str = typer.Argument(
173
+ show_default=False,
174
+ help="Name of the release directive",
175
+ ),
176
+ channel: str = typer.Option(
177
+ DEFAULT_CHANNEL,
178
+ help="Name of the release channel to use",
179
+ ),
180
+ target_accounts: str = typer.Option(
181
+ show_default=False,
182
+ help="List of the accounts to add to the release directive. Format must be `org1.account1,org2.account2`",
183
+ ),
184
+ **options,
185
+ ) -> CommandResult:
186
+ """
187
+ Adds accounts to a release directive.
188
+ """
189
+
190
+ cli_context = get_cli_context()
191
+ ws = WorkspaceManager(
192
+ project_definition=cli_context.project_definition,
193
+ project_root=cli_context.project_root,
194
+ )
195
+ package_id = options["package_entity_id"]
196
+ ws.perform_action(
197
+ package_id,
198
+ EntityActions.RELEASE_DIRECTIVE_ADD_ACCOUNTS,
199
+ release_directive=directive,
200
+ target_accounts=target_accounts.split(","),
201
+ release_channel=channel,
202
+ )
203
+
204
+ return MessageResult("Successfully added accounts to the release directive.")
205
+
206
+
207
+ @app.command("remove-accounts", requires_connection=True)
208
+ @with_project_definition()
209
+ @force_project_definition_v2()
210
+ def release_directive_remove_accounts(
211
+ directive: str = typer.Argument(
212
+ show_default=False,
213
+ help="Name of the release directive",
214
+ ),
215
+ channel: str = typer.Option(
216
+ DEFAULT_CHANNEL,
217
+ help="Name of the release channel to use",
218
+ ),
219
+ target_accounts: str = typer.Option(
220
+ show_default=False,
221
+ help="List of the accounts to remove from the release directive. Format must be `org1.account1,org2.account2`",
222
+ ),
223
+ **options,
224
+ ) -> CommandResult:
225
+ """
226
+ Removes accounts from a release directive.
227
+ """
228
+
229
+ cli_context = get_cli_context()
230
+ ws = WorkspaceManager(
231
+ project_definition=cli_context.project_definition,
232
+ project_root=cli_context.project_root,
233
+ )
234
+ package_id = options["package_entity_id"]
235
+ ws.perform_action(
236
+ package_id,
237
+ EntityActions.RELEASE_DIRECTIVE_REMOVE_ACCOUNTS,
238
+ release_directive=directive,
239
+ target_accounts=target_accounts.split(","),
240
+ release_channel=channel,
241
+ )
242
+
243
+ return MessageResult("Successfully removed accounts from the release directive.")