Skip to content

Commit

Permalink
Add --from-stage option to version create
Browse files Browse the repository at this point in the history
  • Loading branch information
sfc-gh-melnacouzi committed Jan 8, 2025
1 parent 21d864a commit ab0a5c2
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 12 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* `snow app release-directive set`
* `snow app release-directive unset`
* `snow app version create` now returns version, patch, and label in JSON format.
* Add `--from-stage` flag to `snow app version create` to allow version creation from the content of the stage without re-syncing to the stage.
* Add support for release channels:
* Add support for release channels feature in native app version creation/drop.
* Add ability to specify release channel when creating application instance from release directive: `snow app run --from-release-directive --channel=<channel>`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ def action_version_create(
skip_git_check: bool,
interactive: bool,
force: bool,
from_stage: Optional[bool],
*args,
**kwargs,
) -> VersionInfo:
Expand Down Expand Up @@ -476,18 +477,29 @@ def action_version_create(
if git_policy.should_proceed():
self.check_index_changes_in_git_repo(policy=policy, interactive=interactive)

self._deploy(
action_ctx=action_ctx,
bundle_map=bundle_map,
prune=True,
recursive=True,
paths=[],
print_diff=True,
validate=True,
stage_fqn=self.stage_fqn,
interactive=interactive,
force=force,
)
# if user is asking to create the version from the current stage,
# then do not re-deploy the artifacts or touch the stage
if from_stage:
# verify package exists:
show_obj_row = self.get_existing_app_pkg_info()
if not show_obj_row:
raise ClickException(
"Cannot create version from stage because the application package does not exist yet. "
"Try removing --from-stage flag or executing `snow app deploy` to deploy the application package first."
)
else:
self._deploy(
action_ctx=action_ctx,
bundle_map=bundle_map,
prune=True,
recursive=True,
paths=[],
print_diff=True,
validate=True,
stage_fqn=self.stage_fqn,
interactive=interactive,
force=force,
)

# Warn if the version exists in a release directive(s)
try:
Expand Down
7 changes: 7 additions & 0 deletions src/snowflake/cli/_plugins/nativeapp/version/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ def create(
help="When enabled, the Snowflake CLI skips checking if your project has any untracked or stages files in git. Default: unset.",
is_flag=True,
),
from_stage: bool = typer.Option(
False,
"--from-stage",
help="When enabled, the Snowflake CLI creates a version from the current application package stage without syncing to the stage first.",
is_flag=True,
),
interactive: bool = InteractiveOption,
force: Optional[bool] = ForceOption,
**options,
Expand All @@ -95,6 +101,7 @@ def create(
force=force,
interactive=interactive,
skip_git_check=skip_git_check,
from_stage=from_stage,
)

message = "Version create is now complete."
Expand Down
1 change: 1 addition & 0 deletions src/snowflake/cli/_plugins/workspace/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ def version_create(
skip_git_check=skip_git_check,
interactive=interactive,
force=force,
from_stage=False,
)


Expand Down
7 changes: 7 additions & 0 deletions tests/__snapshots__/test_help_messages.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,13 @@
| untracked or stages |
| files in git. Default: |
| unset. |
| --from-stage When enabled, the |
| Snowflake CLI creates |
| a version from the |
| current application |
| package stage without |
| syncing to the stage |
| first. |
| --interactive --no-interactive When enabled, this |
| option displays |
| prompts even if the |
Expand Down
63 changes: 63 additions & 0 deletions tests/nativeapp/test_version_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@

from tests.nativeapp.factories import ApplicationPackageEntityModelFactory, PdfV2Factory
from tests.nativeapp.utils import (
APP_PACKAGE_ENTITY_GET_EXISTING_APP_PKG_INFO,
APPLICATION_PACKAGE_ENTITY_MODULE,
SQL_EXECUTOR_EXECUTE,
SQL_FACADE,
SQL_FACADE_CREATE_VERSION,
SQL_FACADE_SHOW_RELEASE_DIRECTIVES,
mock_execute_helper,
mock_snowflake_yml_file_v2,
)
Expand All @@ -64,6 +66,7 @@ def _version_create(
skip_git_check: bool,
label: str | None = None,
console: AbstractConsole | None = None,
from_stage: bool = False,
):
dm = DefinitionManager()
pd = dm.project_definition
Expand All @@ -83,6 +86,7 @@ def _version_create(
force=force,
interactive=interactive,
skip_git_check=skip_git_check,
from_stage=from_stage,
)


Expand Down Expand Up @@ -931,3 +935,62 @@ def test_patch_from_manifest(
mock_console.warning.assert_called_with(
f"Cannot resolve version. Found patch: {manifest_patch} in manifest.yml which is different from provided patch {cli_patch}."
)


@mock.patch(SQL_FACADE_CREATE_VERSION)
@mock.patch(SQL_FACADE_SHOW_RELEASE_DIRECTIVES, return_value=[])
@mock.patch.object(ApplicationPackageEntity, "_deploy")
@mock.patch(
APP_PACKAGE_ENTITY_GET_EXISTING_APP_PKG_INFO, return_value=[{"name": "app_pkg"}]
)
@mock.patch.object(
ApplicationPackageEntity, "check_index_changes_in_git_repo", return_value=None
)
@mock.patch.object(
ApplicationPackageEntity, "get_existing_version_info", return_value=None
)
@mock.patch.object(ApplicationPackageEntity, "_bundle")
def test_action_version_create_from_stage(
mock_bundle,
mock_get_existing_version_info,
mock_check_git,
mock_get_existing_pkg_info,
mock_deploy,
mock_show_release_directives,
mock_create_version,
application_package_entity,
action_context,
):
pkg_model = application_package_entity._entity_model # noqa SLF001
pkg_model.meta.role = "package_role"

version = "v1"
result = application_package_entity.action_version_create(
action_ctx=action_context,
version=version,
patch=None,
label=None,
skip_git_check=False,
interactive=False,
force=False,
from_stage=True,
)

assert result == VersionInfo(version, 0, None)

mock_check_git.assert_called_once()
mock_show_release_directives.assert_called_once_with(
package_name=pkg_model.fqn.name, role=pkg_model.meta.role
)
mock_get_existing_version_info.assert_called_once_with(version)
mock_bundle.assert_called_once()
mock_create_version.assert_called_once_with(
package_name=pkg_model.fqn.name,
version=version,
stage_fqn=application_package_entity.stage_fqn,
role=pkg_model.meta.role,
label=None,
)

# Deploy should not be called with --from-stage
mock_deploy.assert_not_called()
82 changes: 82 additions & 0 deletions tests_integration/nativeapp/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,85 @@ def test_version_create_with_json_result(runner, nativeapp_project_directory):
"label": None,
"message": "Version create is now complete.",
}


@pytest.mark.integration
def test_version_from_stage(runner, nativeapp_project_directory):
with nativeapp_project_directory("napp_init_v2"):
# Deploy:
result = runner.invoke_with_connection_json(["app", "deploy"])
assert result.exit_code == 0

# Add a file and make sure it shows up in the diff against the stage:
with open("app/TEST_UPDATE.md", "w") as f:
f.write("Hello world!")
result = runner.invoke_with_connection(["app", "diff"])
assert result.exit_code == 0
assert "TEST_UPDATE.md" in result.output

# Test version creation:
result = runner.invoke_with_connection_json(
[
"app",
"version",
"create",
"v1",
"--force",
"--skip-git-check",
"--from-stage",
]
)
assert result.exit_code == 0
assert result.json == {
"version": "v1",
"patch": 0,
"label": None,
"message": "Version create is now complete.",
}

# Make sure the file still hasn't been deployed yet:
result = runner.invoke_with_connection(["app", "diff"])
assert result.exit_code == 0
assert "TEST_UPDATE.md" in result.output

# Create patch:
result = runner.invoke_with_connection_json(
[
"app",
"version",
"create",
"v1",
"--force",
"--skip-git-check",
"--from-stage",
]
)
assert result.exit_code == 0
assert result.json == {
"version": "v1",
"patch": 1,
"label": None,
"message": "Version create is now complete.",
}

# Make sure the file still hasn't been deployed yet:
result = runner.invoke_with_connection(["app", "diff"])
assert result.exit_code == 0
assert "TEST_UPDATE.md" in result.output

# Create patch but don't use --from-stage:
result = runner.invoke_with_connection_json(
["app", "version", "create", "v1", "--force", "--skip-git-check"]
)
assert result.exit_code == 0
assert result.json == {
"version": "v1",
"patch": 2,
"label": None,
"message": "Version create is now complete.",
}

# Make sure the file has been actually deployed:
result = runner.invoke_with_connection(["app", "diff"])
assert result.exit_code == 0
assert "TEST_UPDATE.md" not in result.output

0 comments on commit ab0a5c2

Please sign in to comment.