diff --git a/src/fastapi_cloud_cli/commands/deploy.py b/src/fastapi_cloud_cli/commands/deploy.py index 18146784..5b6536f5 100644 --- a/src/fastapi_cloud_cli/commands/deploy.py +++ b/src/fastapi_cloud_cli/commands/deploy.py @@ -294,6 +294,8 @@ def _configure_app(toolkit: RichToolkit, path_to_deploy: Path) -> AppConfig: toolkit.print_line() + selected_app: AppResponse | None = None + if not create_new_app: with toolkit.progress("Fetching apps...") as progress: with handle_http_errors( @@ -310,18 +312,45 @@ def _configure_app(toolkit: RichToolkit, path_to_deploy: Path) -> AppConfig: raise typer.Exit(1) - app = toolkit.ask( + selected_app = toolkit.ask( "Select the app you want to deploy to:", options=[Option({"name": app.slug, "value": app}) for app in apps], ) - else: - app_name = toolkit.input( + + app_name = ( + selected_app.slug + if selected_app + else toolkit.input( title="What's your app name?", default=_get_app_name(path_to_deploy), ) + ) - toolkit.print_line() + toolkit.print_line() + + toolkit.print("Deployment configuration:", tag="summary") + toolkit.print_line() + toolkit.print(f"Team: [bold]{team.name}[/bold]") + toolkit.print(f"App name: [bold]{app_name}[/bold]") + toolkit.print_line() + choice = toolkit.ask( + "Does everything look right?", + tag="confirm", + options=[ + Option({"name": "Yes, start the deployment!", "value": "deploy"}), + Option({"name": "No, let me start over", "value": "cancel"}), + ], + ) + toolkit.print_line() + + if choice == "cancel": + toolkit.print("Deployment cancelled.") + raise typer.Exit(0) + + if selected_app: # pragma: no cover + app = selected_app + else: with toolkit.progress(title="Creating app...") as progress: with handle_http_errors(progress): app = _create_app(team.id, app_name) diff --git a/tests/test_cli_deploy.py b/tests/test_cli_deploy.py index 33976ed2..2b094b6d 100644 --- a/tests/test_cli_deploy.py +++ b/tests/test_cli_deploy.py @@ -316,7 +316,7 @@ def test_asks_for_app_name_after_team( def test_creates_app_on_backend( logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter ) -> None: - steps = [Keys.ENTER, Keys.ENTER, *"demo", Keys.ENTER] + steps = [Keys.ENTER, Keys.ENTER, *"demo", Keys.ENTER, Keys.ENTER] team = _get_random_team() @@ -344,6 +344,40 @@ def test_creates_app_on_backend( assert "App created successfully" in result.output +@pytest.mark.respx(base_url=settings.base_api_url) +def test_cancels_deployment_when_user_selects_no( + logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter +) -> None: + steps = [ + Keys.ENTER, + Keys.ENTER, + *"demo", + Keys.ENTER, + Keys.DOWN_ARROW, + Keys.ENTER, + ] + + team = _get_random_team() + + respx_mock.get("/teams/").mock( + return_value=Response( + 200, + json={"data": [team]}, + ) + ) + + with ( + changing_dir(tmp_path), + patch("rich_toolkit.container.getchar") as mock_getchar, + ): + mock_getchar.side_effect = steps + + result = runner.invoke(app, ["deploy"]) + + assert result.exit_code == 0 + assert "Deployment cancelled." in result.output + + @pytest.mark.respx(base_url=settings.base_api_url) def test_uses_existing_app( logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter @@ -383,6 +417,7 @@ def test_exits_successfully_when_deployment_is_done( Keys.ENTER, *"demo", Keys.ENTER, + Keys.ENTER, ] team_data = _get_random_team() @@ -633,6 +668,7 @@ def _deploy_without_waiting(respx_mock: respx.MockRouter, tmp_path: Path) -> Res Keys.ENTER, *"demo", Keys.ENTER, + Keys.ENTER, ] team_data = _get_random_team()