diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 688711970..afb992325 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -39,7 +39,7 @@ * Added support for external access (api integrations and secrets) in Streamlit. * Added support for `<% ... %>` syntax in SQL templating. * Support multiple Streamlit application in single snowflake.yml project definition file. -* Added `snow ws migrate` command to migrate `snowflake.yml` file from V1 to V2. +* Added `snow helpers v1-to-v2` command to migrate `snowflake.yml` file from V1 to V2. * Added `--package-entity-id` and `--app-entity-id` options to `snow app` commands to allow targeting specific entities when the `definition_version` in `snowflake.yml` is `2` or higher and it contains multiple `application package` or `application` entities. * Added templates expansion of arbitrary files for Native Apps through `templates` processor. * Added `SNOWFLAKE_..._PRIVATE_KEY_RAW` environment variable to pass private key as a raw string. diff --git a/src/snowflake/cli/_app/commands_registration/builtin_plugins.py b/src/snowflake/cli/_app/commands_registration/builtin_plugins.py index 018f41a94..072955ae4 100644 --- a/src/snowflake/cli/_app/commands_registration/builtin_plugins.py +++ b/src/snowflake/cli/_app/commands_registration/builtin_plugins.py @@ -15,6 +15,7 @@ from snowflake.cli._plugins.connection import plugin_spec as connection_plugin_spec from snowflake.cli._plugins.cortex import plugin_spec as cortex_plugin_spec from snowflake.cli._plugins.git import plugin_spec as git_plugin_spec +from snowflake.cli._plugins.helpers import plugin_spec as migrate_plugin_spec from snowflake.cli._plugins.init import plugin_spec as init_plugin_spec from snowflake.cli._plugins.nativeapp import plugin_spec as nativeapp_plugin_spec from snowflake.cli._plugins.notebook import plugin_spec as notebook_plugin_spec @@ -31,6 +32,7 @@ def get_builtin_plugin_name_to_plugin_spec(): plugin_specs = { "connection": connection_plugin_spec, + "helpers": migrate_plugin_spec, "spcs": spcs_plugin_spec, "nativeapp": nativeapp_plugin_spec, "object": object_plugin_spec, diff --git a/src/snowflake/cli/_plugins/helpers/__init__.py b/src/snowflake/cli/_plugins/helpers/__init__.py new file mode 100644 index 000000000..ada0a4e13 --- /dev/null +++ b/src/snowflake/cli/_plugins/helpers/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 Snowflake Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/snowflake/cli/_plugins/helpers/commands.py b/src/snowflake/cli/_plugins/helpers/commands.py new file mode 100644 index 000000000..4799f2936 --- /dev/null +++ b/src/snowflake/cli/_plugins/helpers/commands.py @@ -0,0 +1,57 @@ +# Copyright (c) 2024 Snowflake Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import typer +import yaml +from snowflake.cli.api.commands.snow_typer import SnowTyperFactory +from snowflake.cli.api.output.types import MessageResult +from snowflake.cli.api.project.definition_conversion import ( + convert_project_definition_to_v2, +) +from snowflake.cli.api.project.definition_manager import DefinitionManager +from snowflake.cli.api.secure_path import SecurePath + +app = SnowTyperFactory( + name="helpers", + help="Helper commands.", +) + + +@app.command() +def v1_to_v2( + accept_templates: bool = typer.Option( + False, "-t", "--accept-templates", help="Allows the migration of templates." + ), + **options, +): + """Migrates the Snowpark, Streamlit, and Native App project definition files from V1 to V2.""" + manager = DefinitionManager() + pd = manager.unrendered_project_definition + + if pd.meets_version_requirement("2"): + return MessageResult("Project definition is already at version 2.") + + pd_v2 = convert_project_definition_to_v2(manager.project_root, pd, accept_templates) + + SecurePath("snowflake.yml").rename("snowflake_V1.yml") + with open("snowflake.yml", "w") as file: + yaml.dump( + pd_v2.model_dump( + exclude_unset=True, exclude_none=True, mode="json", by_alias=True + ), + file, + ) + return MessageResult("Project definition migrated to version 2.") diff --git a/src/snowflake/cli/_plugins/helpers/plugin_spec.py b/src/snowflake/cli/_plugins/helpers/plugin_spec.py new file mode 100644 index 000000000..492cf65c9 --- /dev/null +++ b/src/snowflake/cli/_plugins/helpers/plugin_spec.py @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Snowflake Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from snowflake.cli._plugins.helpers import commands +from snowflake.cli.api.plugins.command import ( + SNOWCLI_ROOT_COMMAND_PATH, + CommandSpec, + CommandType, + plugin_hook_impl, +) + + +@plugin_hook_impl +def command_spec(): + return CommandSpec( + parent_command_path=SNOWCLI_ROOT_COMMAND_PATH, + command_type=CommandType.COMMAND_GROUP, + typer_instance=commands.app.create_instance(), + ) diff --git a/src/snowflake/cli/_plugins/workspace/commands.py b/src/snowflake/cli/_plugins/workspace/commands.py index 3d45f0cc6..99c602e37 100644 --- a/src/snowflake/cli/_plugins/workspace/commands.py +++ b/src/snowflake/cli/_plugins/workspace/commands.py @@ -20,7 +20,6 @@ from typing import List, Optional import typer -import yaml from snowflake.cli._plugins.nativeapp.artifacts import BundleMap from snowflake.cli._plugins.nativeapp.common_flags import ( ForceOption, @@ -34,46 +33,15 @@ from snowflake.cli.api.entities.common import EntityActions from snowflake.cli.api.exceptions import IncompatibleParametersError from snowflake.cli.api.output.types import MessageResult, QueryResult -from snowflake.cli.api.project.definition_conversion import ( - convert_project_definition_to_v2, -) -from snowflake.cli.api.project.definition_manager import DefinitionManager -from snowflake.cli.api.secure_path import SecurePath ws = SnowTyperFactory( name="ws", help="Deploy and interact with snowflake.yml-based entities.", + is_hidden=lambda: True, ) log = logging.getLogger(__name__) -@ws.command() -def migrate( - accept_templates: bool = typer.Option( - False, "-t", "--accept-templates", help="Allows the migration of templates." - ), - **options, -): - """Migrates the Snowpark, Streamlit, and Native App project definition files from V1 to V2.""" - manager = DefinitionManager() - pd = manager.unrendered_project_definition - - if pd.meets_version_requirement("2"): - return MessageResult("Project definition is already at version 2.") - - pd_v2 = convert_project_definition_to_v2(manager.project_root, pd, accept_templates) - - SecurePath("snowflake.yml").rename("snowflake_V1.yml") - with open("snowflake.yml", "w") as file: - yaml.dump( - pd_v2.model_dump( - exclude_unset=True, exclude_none=True, mode="json", by_alias=True - ), - file, - ) - return MessageResult("Project definition migrated to version 2.") - - @ws.command(requires_connection=True, hidden=True) @with_project_definition() def bundle( diff --git a/tests/__snapshots__/test_help_messages.ambr b/tests/__snapshots__/test_help_messages.ambr index 2c0042a7d..fa32bdcdc 100644 --- a/tests/__snapshots__/test_help_messages.ambr +++ b/tests/__snapshots__/test_help_messages.ambr @@ -25,6 +25,7 @@ | connection Manages connections to Snowflake. | | cortex Provides access to Snowflake Cortex. | | git Manages git repositories in Snowflake. | + | helpers Helper commands. | | init Creates project directory from template. | | notebook Manages notebooks in Snowflake. | | object Manages Snowflake objects like warehouses and stages | @@ -34,7 +35,6 @@ | sql Executes Snowflake query. | | stage Manages stages. | | streamlit Manages a Streamlit app in Snowflake. | - | ws Deploy and interact with snowflake.yml-based entities. | +------------------------------------------------------------------------------+ @@ -8161,6 +8161,7 @@ | connection Manages connections to Snowflake. | | cortex Provides access to Snowflake Cortex. | | git Manages git repositories in Snowflake. | + | helpers Helper commands. | | init Creates project directory from template. | | notebook Manages notebooks in Snowflake. | | object Manages Snowflake objects like warehouses and stages | @@ -8170,7 +8171,6 @@ | sql Executes Snowflake query. | | stage Manages stages. | | streamlit Manages a Streamlit app in Snowflake. | - | ws Deploy and interact with snowflake.yml-based entities. | +------------------------------------------------------------------------------+ diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/helpers/__snapshots__/test_v1_to_v2.ambr b/tests/helpers/__snapshots__/test_v1_to_v2.ambr new file mode 100644 index 000000000..913229f45 --- /dev/null +++ b/tests/helpers/__snapshots__/test_v1_to_v2.ambr @@ -0,0 +1,324 @@ +# serializer version: 1 +# name: test_if_template_is_not_rendered_during_migration_with_option_checked[snowpark_templated_v1] + ''' + definition_version: '2' + entities: + hello_function: + artifacts: + - dest: + src: <% ctx.env.project_source %> + external_access_integrations: [] + handler: functions.hello_function + identifier: + name: hello_function + imports: [] + meta: + use_mixins: + - snowpark_shared + returns: string + secrets: {} + signature: + - name: name + type: string + stage: + type: function + hello_procedure: + artifacts: + - dest: + src: <% ctx.env.project_source %> + execute_as_caller: false + external_access_integrations: [] + handler: procedures.hello_procedure + identifier: + name: hello_procedure + imports: [] + meta: + use_mixins: + - snowpark_shared + returns: string + secrets: {} + signature: + - name: name + type: string + stage: + type: procedure + test_procedure: + artifacts: + - dest: + src: <% ctx.env.project_source %> + execute_as_caller: false + external_access_integrations: [] + handler: procedures.test_procedure + identifier: + name: test_procedure + imports: [] + meta: + use_mixins: + - snowpark_shared + returns: string + secrets: {} + signature: '' + stage: + type: procedure + env: + project_source: app/ + mixins: + snowpark_shared: + artifacts: + - dest: + src: <% ctx.env.project_source %> + stage: + + ''' +# --- +# name: test_if_template_is_not_rendered_during_migration_with_option_checked[snowpark_templated_v1].1 + ''' + definition_version: "1.1" + snowpark: + project_name: "" + stage_name: "" + src: <% ctx.env.project_source %> + functions: + - name: hello_function + handler: "functions.hello_function" + signature: + - name: "name" + type: "string" + returns: string + procedures: + - name: hello_procedure + handler: "procedures.hello_procedure" + signature: + - name: "name" + type: "string" + returns: string + - name: test_procedure + handler: "procedures.test_procedure" + signature: "" + returns: string + env: + project_source: "app/" + + ''' +# --- +# name: test_if_template_is_not_rendered_during_migration_with_option_checked[streamlit_templated_v1] + ''' + definition_version: '2' + entities: + streamlit_entity_1: + artifacts: + - streamlit_app.py + - environment.yml + - pages + - common/hello.py + identifier: + name: + main_file: streamlit_app.py + pages_dir: pages + query_warehouse: + stage: + title: <% ctx.env.streamlit_title %> + type: streamlit + env: + streamlit_title: My Fancy Streamlit + + ''' +# --- +# name: test_if_template_is_not_rendered_during_migration_with_option_checked[streamlit_templated_v1].1 + ''' + definition_version: "1.1" + streamlit: + name: + stage: + query_warehouse: + main_file: streamlit_app.py + env_file: environment.yml + pages_dir: pages/ + title: <% ctx.env.streamlit_title %> + additional_source_files: + - common/hello.py + env: + streamlit_title: "My Fancy Streamlit" + + ''' +# --- +# name: test_migrating_a_file_with_duplicated_keys_raises_an_error[\n - name: test\n handler: "test"\n signature: ""\n returns: string\n handler: test\n runtime: "3.10"\n ] + ''' + +- Error ----------------------------------------------------------------------+ + | While loading the project definition file, duplicate key was found: handler | + +------------------------------------------------------------------------------+ + + ''' +# --- +# name: test_migrating_a_file_with_duplicated_keys_raises_an_error[\n - name: test\n handler: "test"\n signature: ""\n returns: string\n runtime: "3.10"\n ] + ''' + +- Error ----------------------------------------------------------------------+ + | Entity with name test seems to be duplicated. Please rename it and try | + | again. | + +------------------------------------------------------------------------------+ + + ''' +# --- +# name: test_migrating_a_file_with_duplicated_keys_raises_an_error[\nstreamlit:\n name: test\n stage: streamlit\n query_warehouse: test_warehouse\n main_file: "streamlit_app.py"\n title: "My Fancy Streamlit"\n ] + ''' + +- Error ----------------------------------------------------------------------+ + | In your project, streamlit and snowpark entities share the same name. Please | + | rename them and try again. | + +------------------------------------------------------------------------------+ + + ''' +# --- +# name: test_migrations_with_multiple_entities + ''' + definition_version: '2' + entities: + app: + from: + target: pkg + identifier: myapp_app + meta: + warehouse: app_wh + type: application + func1: + artifacts: + - dest: my_snowpark_project + src: app + external_access_integrations: [] + handler: app.func1_handler + identifier: + name: func1 + imports: [] + meta: + use_mixins: + - snowpark_shared + returns: string + runtime: '3.10' + secrets: {} + signature: + - default: default value + name: a + type: string + - name: b + type: variant + stage: dev_deployment + type: function + pkg: + artifacts: + - dest: ./ + src: app/* + - dest: ./ + processors: + - name: native app setup + - name: templates + properties: + foo: bar + src: to_process/* + bundle_root: output/bundle/ + deploy_root: output/deploy/ + distribution: external + generated_root: __generated/ + identifier: <% fn.concat_ids('myapp', '_pkg_', fn.sanitize_id(fn.get_username('unknown_user')) + | lower) %> + manifest: app/manifest.yml + meta: + role: pkg_role + scratch_stage: app_src.scratch + stage: app_src.stage + type: application package + procedureName: + artifacts: + - dest: my_snowpark_project + src: app + execute_as_caller: false + external_access_integrations: [] + handler: hello + identifier: + name: procedureName + imports: [] + meta: + use_mixins: + - snowpark_shared + returns: string + secrets: {} + signature: + - name: name + type: string + stage: dev_deployment + type: procedure + test_streamlit: + artifacts: + - streamlit_app.py + - environment.yml + - pages + identifier: + name: test_streamlit + main_file: streamlit_app.py + pages_dir: None + query_warehouse: test_warehouse + stage: streamlit + title: My Fancy Streamlit + type: streamlit + mixins: + snowpark_shared: + artifacts: + - dest: my_snowpark_project + src: app/ + stage: dev_deployment + + ''' +# --- +# name: test_migrations_with_multiple_entities.1 + ''' + definition_version: 1 + streamlit: + name: test_streamlit + stage: streamlit + query_warehouse: test_warehouse + main_file: "streamlit_app.py" + title: "My Fancy Streamlit" + snowpark: + project_name: "my_snowpark_project" + stage_name: "dev_deployment" + src: "app/" + functions: + - name: func1 + handler: "app.func1_handler" + signature: + - name: "a" + type: "string" + default: "default value" + - name: "b" + type: "variant" + returns: string + runtime: 3.10 + procedures: + - name: procedureName + handler: "hello" + signature: + - name: "name" + type: "string" + returns: string + native_app: + name: myapp + source_stage: app_src.stage + scratch_stage: app_src.scratch + artifacts: + - src: app/* + dest: ./ + - src: to_process/* + dest: ./ + processors: + - native app setup + - name: templates + properties: + foo: bar + package: + role: pkg_role + distribution: external + application: + name: myapp_app + warehouse: app_wh + debug: true + + ''' +# --- diff --git a/tests/helpers/test_v1_to_v2.py b/tests/helpers/test_v1_to_v2.py new file mode 100644 index 000000000..6a230dac9 --- /dev/null +++ b/tests/helpers/test_v1_to_v2.py @@ -0,0 +1,143 @@ +import logging +from pathlib import Path +from textwrap import dedent + +import pytest +import yaml + + +def test_migration_already_v2( + runner, + project_directory, +): + with project_directory("migration_already_v2"): + result = runner.invoke(["helpers", "v1-to-v2"]) + + assert result.exit_code == 0 + assert "Project definition is already at version 2." in result.output + + +def test_migrations_with_multiple_entities( + runner, project_directory, os_agnostic_snapshot +): + with project_directory("migration_multiple_entities"): + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 0 + assert Path("snowflake.yml").read_text() == os_agnostic_snapshot + assert Path("snowflake_V1.yml").read_text() == os_agnostic_snapshot + + +def test_migration_native_app_missing_manifest(runner, project_directory): + with project_directory("migration_multiple_entities") as project_dir: + (project_dir / "app" / "manifest.yml").unlink() + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 1 + assert "manifest.yml file not found" in result.output + + +def test_migration_native_app_no_artifacts(runner, project_directory): + with project_directory("migration_multiple_entities") as project_dir: + with (project_dir / "snowflake.yml").open("r+") as snowflake_yml: + pdf = yaml.safe_load(snowflake_yml) + pdf["native_app"]["artifacts"] = [] + snowflake_yml.seek(0) + yaml.safe_dump(pdf, snowflake_yml) + snowflake_yml.truncate() + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 1 + assert "No artifacts mapping found in project definition" in result.output + assert "Could not bundle Native App artifacts" in result.output + + +def test_migration_native_app_package_scripts(runner, project_directory): + with project_directory("migration_package_scripts") as project_dir: + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 0 + package_scripts_dir = project_dir / "package_scripts" + for file in package_scripts_dir.iterdir(): + assert file.read_text() == dedent( + """\ + -- Just a demo package script, won't actually be executed in tests + select * from <% ctx.entities.pkg.identifier %>.my_schema.my_table + """ + ) + + +@pytest.mark.parametrize( + "project_directory_name", ["snowpark_templated_v1", "streamlit_templated_v1"] +) +def test_if_template_is_not_rendered_during_migration_with_option_checked( + runner, project_directory, project_directory_name, os_agnostic_snapshot, caplog +): + with project_directory(project_directory_name): + with caplog.at_level(logging.WARNING): + result = runner.invoke(["helpers", "v1-to-v2", "--accept-templates"]) + + assert result.exit_code == 0 + assert Path("snowflake.yml").read_text() == os_agnostic_snapshot + assert Path("snowflake_V1.yml").read_text() == os_agnostic_snapshot + assert ( + "Your V1 definition contains templates. We cannot guarantee the correctness of the migration." + in caplog.text + ) + + +@pytest.mark.parametrize( + "project_directory_name", ["snowpark_templated_v1", "streamlit_templated_v1"] +) +def test_if_template_raises_error_during_migrations( + runner, project_directory, project_directory_name, os_agnostic_snapshot +): + with project_directory(project_directory_name): + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 1, result.output + assert "Project definition contains templates" in result.output + + +def test_migration_with_only_envs(project_directory, runner): + with project_directory("sql_templating"): + result = runner.invoke(["helpers", "v1-to-v2"]) + + assert result.exit_code == 0 + + +@pytest.mark.parametrize( + "duplicated_entity", + [ + """ + - name: test + handler: "test" + signature: "" + returns: string + runtime: "3.10" + """, + """ +streamlit: + name: test + stage: streamlit + query_warehouse: test_warehouse + main_file: "streamlit_app.py" + title: "My Fancy Streamlit" + """, + """ + - name: test + handler: "test" + signature: "" + returns: string + handler: test + runtime: "3.10" + """, + ], +) +def test_migrating_a_file_with_duplicated_keys_raises_an_error( + runner, project_directory, os_agnostic_snapshot, duplicated_entity +): + with project_directory("snowpark_procedures") as pd: + definition_path = pd / "snowflake.yml" + + with open(definition_path, "a") as definition_file: + definition_file.write(duplicated_entity) + + result = runner.invoke(["helpers", "v1-to-v2"]) + assert result.exit_code == 1, result.output + assert result.output == os_agnostic_snapshot diff --git a/tests/test_help_messages.py b/tests/test_help_messages.py index 4370d043c..f7f1ed207 100644 --- a/tests/test_help_messages.py +++ b/tests/test_help_messages.py @@ -34,7 +34,7 @@ def iter_through_all_commands(command_groups_only: bool = False): Generator iterating through all commands. Paths are yielded as List[str] """ - ignore_plugins = ["render", "cortex", "workspace"] + ignore_plugins = ["helpers", "cortex", "workspace"] no_command: List[str] = [] yield no_command @@ -67,7 +67,7 @@ def test_help_messages(runner, snapshot, command): Check help messages against the snapshot """ result = runner.invoke(command + ["--help"]) - assert result.exit_code == 0 + assert result.exit_code == 0, result.output assert result.output == snapshot diff --git a/tests/workspace/test_manager.py b/tests/workspace/test_manager.py index a95425f30..f4ecfbf58 100644 --- a/tests/workspace/test_manager.py +++ b/tests/workspace/test_manager.py @@ -13,10 +13,7 @@ # limitations under the License. from __future__ import annotations -import logging import os -from pathlib import Path -from textwrap import dedent from unittest import mock import pytest @@ -86,143 +83,6 @@ def test_bundle_of_invalid_entity_type(temp_dir): ws_manager.perform_action("app", EntityActions.BUNDLE) -def test_migration_already_v2( - runner, - project_directory, -): - with project_directory("migration_already_v2"): - result = runner.invoke(["ws", "migrate"]) - - assert result.exit_code == 0 - assert "Project definition is already at version 2." in result.output - - -def test_migrations_with_multiple_entities( - runner, project_directory, os_agnostic_snapshot -): - with project_directory("migration_multiple_entities"): - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 0 - assert Path("snowflake.yml").read_text() == os_agnostic_snapshot - assert Path("snowflake_V1.yml").read_text() == os_agnostic_snapshot - - -def test_migration_native_app_missing_manifest(runner, project_directory): - with project_directory("migration_multiple_entities") as project_dir: - (project_dir / "app" / "manifest.yml").unlink() - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 1 - assert "manifest.yml file not found" in result.output - - -def test_migration_native_app_no_artifacts(runner, project_directory): - with project_directory("migration_multiple_entities") as project_dir: - with (project_dir / "snowflake.yml").open("r+") as snowflake_yml: - pdf = yaml.safe_load(snowflake_yml) - pdf["native_app"]["artifacts"] = [] - snowflake_yml.seek(0) - yaml.safe_dump(pdf, snowflake_yml) - snowflake_yml.truncate() - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 1 - assert "No artifacts mapping found in project definition" in result.output - assert "Could not bundle Native App artifacts" in result.output - - -def test_migration_native_app_package_scripts(runner, project_directory): - with project_directory("migration_package_scripts") as project_dir: - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 0 - package_scripts_dir = project_dir / "package_scripts" - for file in package_scripts_dir.iterdir(): - assert file.read_text() == dedent( - """\ - -- Just a demo package script, won't actually be executed in tests - select * from <% ctx.entities.pkg.identifier %>.my_schema.my_table - """ - ) - - -@pytest.mark.parametrize( - "project_directory_name", ["snowpark_templated_v1", "streamlit_templated_v1"] -) -def test_if_template_is_not_rendered_during_migration_with_option_checked( - runner, project_directory, project_directory_name, os_agnostic_snapshot, caplog -): - with project_directory(project_directory_name): - with caplog.at_level(logging.WARNING): - result = runner.invoke(["ws", "migrate", "--accept-templates"]) - - assert result.exit_code == 0 - assert Path("snowflake.yml").read_text() == os_agnostic_snapshot - assert Path("snowflake_V1.yml").read_text() == os_agnostic_snapshot - assert ( - "Your V1 definition contains templates. We cannot guarantee the correctness of the migration." - in caplog.text - ) - - -@pytest.mark.parametrize( - "project_directory_name", ["snowpark_templated_v1", "streamlit_templated_v1"] -) -def test_if_template_raises_error_during_migrations( - runner, project_directory, project_directory_name, os_agnostic_snapshot -): - with project_directory(project_directory_name): - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 1 - assert "Project definition contains templates" in result.output - - -def test_migration_with_only_envs(project_directory, runner): - with project_directory("sql_templating"): - result = runner.invoke(["ws", "migrate"]) - - assert result.exit_code == 0 - - -@pytest.mark.parametrize( - "duplicated_entity", - [ - """ - - name: test - handler: "test" - signature: "" - returns: string - runtime: "3.10" - """, - """ -streamlit: - name: test - stage: streamlit - query_warehouse: test_warehouse - main_file: "streamlit_app.py" - title: "My Fancy Streamlit" - """, - """ - - name: test - handler: "test" - signature: "" - returns: string - handler: test - runtime: "3.10" - """, - ], -) -def test_migrating_a_file_with_duplicated_keys_raises_an_error( - runner, project_directory, os_agnostic_snapshot, duplicated_entity -): - with project_directory("snowpark_procedures") as pd: - definition_path = pd / "snowflake.yml" - - with open(definition_path, "a") as definition_file: - definition_file.write(duplicated_entity) - - result = runner.invoke(["ws", "migrate"]) - assert result.exit_code == 1 - assert result.output == os_agnostic_snapshot - - @pytest.mark.parametrize("definition_version", [1, "1.1"]) def test_migrate_nativeapp_fields_with_username( runner, project_directory, definition_version @@ -236,7 +96,7 @@ def test_migrate_nativeapp_fields_with_username( yaml.safe_dump(old_definition, f) f.truncate() - result = runner.invoke(["ws", "migrate", "--accept-templates"]) + result = runner.invoke(["helpers", "v1-to-v2", "--accept-templates"]) assert result.exit_code == 0, result.output with definition_path.open("r") as f: diff --git a/tests_e2e/__snapshots__/test_installation.ambr b/tests_e2e/__snapshots__/test_installation.ambr index a99e2982c..98a512870 100644 --- a/tests_e2e/__snapshots__/test_installation.ambr +++ b/tests_e2e/__snapshots__/test_installation.ambr @@ -35,6 +35,7 @@ | connection Manages connections to Snowflake. | | cortex Provides access to Snowflake Cortex. | | git Manages git repositories in Snowflake. | + | helpers Helper commands. | | init Creates project directory from template. | | notebook Manages notebooks in Snowflake. | | object Manages Snowflake objects like warehouses and stages | @@ -44,7 +45,6 @@ | sql Executes Snowflake query. | | stage Manages stages. | | streamlit Manages a Streamlit app in Snowflake. | - | ws Deploy and interact with snowflake.yml-based entities. | +------------------------------------------------------------------------------+