fastapi-cloud-cli 0.2.1__tar.gz → 0.3.1__tar.gz
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.
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/PKG-INFO +1 -1
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/pyproject.toml +1 -1
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/requirements-tests.txt +1 -1
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/requirements.txt +1 -1
- fastapi_cloud_cli-0.3.1/src/fastapi_cloud_cli/__init__.py +1 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/deploy.py +6 -50
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/env.py +5 -3
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/login.py +1 -1
- fastapi_cloud_cli-0.3.1/tests/test_archive.py +103 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli_deploy.py +0 -95
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_env_set.py +3 -9
- fastapi_cloud_cli-0.2.1/src/fastapi_cloud_cli/__init__.py +0 -1
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/LICENSE +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/README.md +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/scripts/format.sh +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/scripts/lint.sh +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/scripts/test-cov-html.sh +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/scripts/test.sh +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/__main__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/cli.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/logout.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/unlink.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/whoami.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/config.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/logging.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/py.typed +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/apps.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/auth.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/cli.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/config.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/env.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/utils/sentry.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/broken_package/mod/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/broken_package/mod/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/broken_package/utils.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_api/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_api/app/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_api/app/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_app/app/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_app/app/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_app/app/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_main/app/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_main/app/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_main/app/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_main/app/main.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_non_default/app/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app_dir_non_default/app/nondefault.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/main.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/non_default/nonstandard.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/core/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/core/utils.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/mod/__init__.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/mod/api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/mod/app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/package/mod/other.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/single_file_api.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/single_file_app.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/single_file_other.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/conftest.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli_login.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli_logout.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli_unlink.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_cli_whoami.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_config.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_deploy_utils.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_env_delete.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_env_list.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/test_sentry.py +0 -0
- {fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/utils.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.1"
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/deploy.py
RENAMED
|
@@ -25,7 +25,6 @@ from fastapi_cloud_cli.utils.api import APIClient
|
|
|
25
25
|
from fastapi_cloud_cli.utils.apps import AppConfig, get_app_config, write_app_config
|
|
26
26
|
from fastapi_cloud_cli.utils.auth import is_logged_in
|
|
27
27
|
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
|
|
28
|
-
from fastapi_cloud_cli.utils.env import validate_environment_variable_name
|
|
29
28
|
|
|
30
29
|
logger = logging.getLogger(__name__)
|
|
31
30
|
|
|
@@ -49,7 +48,11 @@ def _should_exclude_entry(path: Path) -> bool:
|
|
|
49
48
|
|
|
50
49
|
def archive(path: Path) -> Path:
|
|
51
50
|
logger.debug("Starting archive creation for path: %s", path)
|
|
52
|
-
files = rignore.walk(
|
|
51
|
+
files = rignore.walk(
|
|
52
|
+
path,
|
|
53
|
+
should_exclude_entry=_should_exclude_entry,
|
|
54
|
+
additional_ignore_paths=[".fastapicloudignore"],
|
|
55
|
+
)
|
|
53
56
|
|
|
54
57
|
temp_dir = tempfile.mkdtemp()
|
|
55
58
|
logger.debug("Created temp directory: %s", temp_dir)
|
|
@@ -64,6 +67,7 @@ def archive(path: Path) -> Path:
|
|
|
64
67
|
if filename.is_dir():
|
|
65
68
|
continue
|
|
66
69
|
|
|
70
|
+
logger.debug("Adding %s to archive", filename.relative_to(path))
|
|
67
71
|
tar.add(filename, arcname=filename.relative_to(path))
|
|
68
72
|
file_count += 1
|
|
69
73
|
|
|
@@ -225,12 +229,6 @@ def _get_apps(team_id: str) -> List[AppResponse]:
|
|
|
225
229
|
return [AppResponse.model_validate(app) for app in data]
|
|
226
230
|
|
|
227
231
|
|
|
228
|
-
def _create_environment_variables(app_id: str, env_vars: Dict[str, str]) -> None:
|
|
229
|
-
with APIClient() as client:
|
|
230
|
-
response = client.patch(f"/apps/{app_id}/environment-variables/", json=env_vars)
|
|
231
|
-
response.raise_for_status()
|
|
232
|
-
|
|
233
|
-
|
|
234
232
|
def _stream_build_logs(deployment_id: str) -> Generator[str, None, None]:
|
|
235
233
|
with APIClient() as client:
|
|
236
234
|
with client.stream(
|
|
@@ -395,45 +393,6 @@ def _wait_for_deployment(
|
|
|
395
393
|
last_message_changed_at = time.monotonic() # pragma: no cover
|
|
396
394
|
|
|
397
395
|
|
|
398
|
-
def _setup_environment_variables(toolkit: RichToolkit, app_id: str) -> None:
|
|
399
|
-
if not toolkit.confirm("Do you want to setup environment variables?", tag="env"):
|
|
400
|
-
return
|
|
401
|
-
|
|
402
|
-
toolkit.print_line()
|
|
403
|
-
|
|
404
|
-
env_vars = {}
|
|
405
|
-
|
|
406
|
-
while True:
|
|
407
|
-
key = toolkit.input(
|
|
408
|
-
"Enter the environment variable name: [ENTER to skip]", required=False
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
if key.strip() == "":
|
|
412
|
-
break
|
|
413
|
-
|
|
414
|
-
if not validate_environment_variable_name(key):
|
|
415
|
-
toolkit.print(
|
|
416
|
-
"[error]Invalid environment variable name.",
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
else:
|
|
420
|
-
value = toolkit.input(
|
|
421
|
-
"Enter the environment variable value:", password=True
|
|
422
|
-
)
|
|
423
|
-
|
|
424
|
-
env_vars[key] = value
|
|
425
|
-
|
|
426
|
-
toolkit.print_line()
|
|
427
|
-
|
|
428
|
-
toolkit.print_line()
|
|
429
|
-
|
|
430
|
-
with toolkit.progress("Setting up environment variables...") as progress:
|
|
431
|
-
with handle_http_errors(progress):
|
|
432
|
-
_create_environment_variables(app_id, env_vars)
|
|
433
|
-
|
|
434
|
-
progress.log("Environment variables set up successfully!")
|
|
435
|
-
|
|
436
|
-
|
|
437
396
|
class SignupToWaitingList(BaseModel):
|
|
438
397
|
email: EmailStr
|
|
439
398
|
name: Optional[str] = None
|
|
@@ -601,9 +560,6 @@ def deploy(
|
|
|
601
560
|
logger.debug("No app config found, configuring new app")
|
|
602
561
|
app_config = _configure_app(toolkit, path_to_deploy=path_to_deploy)
|
|
603
562
|
toolkit.print_line()
|
|
604
|
-
|
|
605
|
-
_setup_environment_variables(toolkit, app_config.app_id)
|
|
606
|
-
toolkit.print_line()
|
|
607
563
|
else:
|
|
608
564
|
logger.debug("Existing app config found, proceeding with deployment")
|
|
609
565
|
toolkit.print("Deploying app...")
|
|
@@ -44,11 +44,13 @@ def _delete_environment_variable(app_id: str, name: str) -> bool:
|
|
|
44
44
|
return True
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def _set_environment_variable(
|
|
47
|
+
def _set_environment_variable(
|
|
48
|
+
app_id: str, name: str, value: str, is_secret: bool = False
|
|
49
|
+
) -> None:
|
|
48
50
|
with APIClient() as client:
|
|
49
|
-
response = client.
|
|
51
|
+
response = client.post(
|
|
50
52
|
f"/apps/{app_id}/environment-variables/",
|
|
51
|
-
json={name: value},
|
|
53
|
+
json={"name": name, "value": value, "is_secret": is_secret},
|
|
52
54
|
)
|
|
53
55
|
response.raise_for_status()
|
|
54
56
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import tarfile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from fastapi_cloud_cli.commands.deploy import archive
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_archive_creates_tar_file(tmp_path: Path) -> None:
|
|
8
|
+
(tmp_path / "main.py").write_text("print('hello')")
|
|
9
|
+
(tmp_path / "config.json").write_text('{"key": "value"}')
|
|
10
|
+
(tmp_path / "subdir").mkdir()
|
|
11
|
+
(tmp_path / "subdir" / "utils.py").write_text("def helper(): pass")
|
|
12
|
+
|
|
13
|
+
tar_path = archive(tmp_path)
|
|
14
|
+
|
|
15
|
+
assert tar_path.exists()
|
|
16
|
+
assert tar_path.suffix == ".tar"
|
|
17
|
+
assert tar_path.name.startswith("fastapi-cloud-deploy-")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_archive_excludes_venv_and_similar_folders(tmp_path: Path) -> None:
|
|
21
|
+
"""Should exclude .venv directory from archive."""
|
|
22
|
+
# the only files we want to include
|
|
23
|
+
(tmp_path / "main.py").write_text("print('hello')")
|
|
24
|
+
(tmp_path / "static").mkdir()
|
|
25
|
+
(tmp_path / "static" / "index.html").write_text("<html></html>")
|
|
26
|
+
# virtualenv
|
|
27
|
+
(tmp_path / ".venv").mkdir()
|
|
28
|
+
(tmp_path / ".venv" / "lib").mkdir()
|
|
29
|
+
(tmp_path / ".venv" / "lib" / "package.py").write_text("# package")
|
|
30
|
+
# pycache
|
|
31
|
+
(tmp_path / "__pycache__").mkdir()
|
|
32
|
+
(tmp_path / "__pycache__" / "main.cpython-311.pyc").write_text("bytecode")
|
|
33
|
+
# pyc files
|
|
34
|
+
(tmp_path / "main.pyc").write_text("bytecode")
|
|
35
|
+
# mypy/pytest
|
|
36
|
+
(tmp_path / ".mypy_cache").mkdir()
|
|
37
|
+
(tmp_path / ".mypy_cache" / "file.json").write_text("{}")
|
|
38
|
+
(tmp_path / ".pytest_cache").mkdir()
|
|
39
|
+
(tmp_path / ".pytest_cache" / "cache.db").write_text("data")
|
|
40
|
+
|
|
41
|
+
tar_path = archive(tmp_path)
|
|
42
|
+
|
|
43
|
+
with tarfile.open(tar_path, "r") as tar:
|
|
44
|
+
names = tar.getnames()
|
|
45
|
+
assert set(names) == {"main.py", "static/index.html"}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_archive_preserves_relative_paths(tmp_path: Path) -> None:
|
|
49
|
+
(tmp_path / "src").mkdir()
|
|
50
|
+
(tmp_path / "src" / "app").mkdir()
|
|
51
|
+
(tmp_path / "src" / "app" / "main.py").write_text("print('hello')")
|
|
52
|
+
|
|
53
|
+
tar_path = archive(tmp_path)
|
|
54
|
+
|
|
55
|
+
with tarfile.open(tar_path, "r") as tar:
|
|
56
|
+
names = tar.getnames()
|
|
57
|
+
assert names == ["src/app/main.py"]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_archive_respects_fastapicloudignore(tmp_path: Path) -> None:
|
|
61
|
+
"""Should exclude files specified in .fastapicloudignore."""
|
|
62
|
+
# Create test files
|
|
63
|
+
(tmp_path / "main.py").write_text("print('hello')")
|
|
64
|
+
(tmp_path / "config.py").write_text("CONFIG = 'value'")
|
|
65
|
+
(tmp_path / "secrets.env").write_text("SECRET_KEY=xyz")
|
|
66
|
+
(tmp_path / "data").mkdir()
|
|
67
|
+
(tmp_path / "data" / "file.txt").write_text("data")
|
|
68
|
+
|
|
69
|
+
# Create .fastapicloudignore file
|
|
70
|
+
(tmp_path / ".fastapicloudignore").write_text("secrets.env\ndata/\n")
|
|
71
|
+
|
|
72
|
+
# Create archive
|
|
73
|
+
tar_path = archive(tmp_path)
|
|
74
|
+
|
|
75
|
+
# Verify ignored files are excluded
|
|
76
|
+
with tarfile.open(tar_path, "r") as tar:
|
|
77
|
+
names = tar.getnames()
|
|
78
|
+
assert set(names) == {
|
|
79
|
+
"main.py",
|
|
80
|
+
"config.py",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_archive_respects_fastapicloudignore_unignore(tmp_path: Path) -> None:
|
|
85
|
+
"""Test we can use .fastapicloudignore to unignore files inside .gitignore"""
|
|
86
|
+
# Create test files
|
|
87
|
+
(tmp_path / "main.py").write_text("print('hello')")
|
|
88
|
+
(tmp_path / "static/build").mkdir(exist_ok=True, parents=True)
|
|
89
|
+
(tmp_path / "static/build/style.css").write_text("body { background: #bada55 }")
|
|
90
|
+
# Rignore needs a .git folder to make .gitignore work
|
|
91
|
+
(tmp_path / ".git").mkdir(exist_ok=True, parents=True)
|
|
92
|
+
(tmp_path / ".gitignore").write_text("build/")
|
|
93
|
+
|
|
94
|
+
# Create .fastapicloudignore file
|
|
95
|
+
(tmp_path / ".fastapicloudignore").write_text("!static/build")
|
|
96
|
+
|
|
97
|
+
# Create archive
|
|
98
|
+
tar_path = archive(tmp_path)
|
|
99
|
+
|
|
100
|
+
# Verify ignored files are excluded
|
|
101
|
+
with tarfile.open(tar_path, "r") as tar:
|
|
102
|
+
names = tar.getnames()
|
|
103
|
+
assert set(names) == {"main.py", "static/build/style.css"}
|
|
@@ -386,8 +386,6 @@ def test_exits_successfully_when_deployment_is_done(
|
|
|
386
386
|
Keys.ENTER,
|
|
387
387
|
*"demo",
|
|
388
388
|
Keys.ENTER,
|
|
389
|
-
Keys.RIGHT_ARROW,
|
|
390
|
-
Keys.ENTER,
|
|
391
389
|
]
|
|
392
390
|
|
|
393
391
|
team_data = _get_random_team()
|
|
@@ -642,8 +640,6 @@ def _deploy_without_waiting(respx_mock: respx.MockRouter, tmp_path: Path) -> Res
|
|
|
642
640
|
Keys.ENTER,
|
|
643
641
|
*"demo",
|
|
644
642
|
Keys.ENTER,
|
|
645
|
-
Keys.RIGHT_ARROW,
|
|
646
|
-
Keys.ENTER,
|
|
647
643
|
]
|
|
648
644
|
|
|
649
645
|
team_data = _get_random_team()
|
|
@@ -730,97 +726,6 @@ def test_does_not_duplicate_entry_in_git_ignore(
|
|
|
730
726
|
assert git_ignore_path.read_text() == ".fastapicloud\n"
|
|
731
727
|
|
|
732
728
|
|
|
733
|
-
@pytest.mark.respx(base_url=settings.base_api_url)
|
|
734
|
-
def test_creates_environment_variables_during_app_setup(
|
|
735
|
-
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
|
|
736
|
-
) -> None:
|
|
737
|
-
steps = [
|
|
738
|
-
Keys.ENTER, # Setup and deploy
|
|
739
|
-
Keys.ENTER, # Select team
|
|
740
|
-
Keys.ENTER, # Create new app
|
|
741
|
-
*"demo", # App name
|
|
742
|
-
Keys.ENTER,
|
|
743
|
-
Keys.ENTER, # Setup environment variables (Yes)
|
|
744
|
-
*"API_KEY", # Environment variable name
|
|
745
|
-
Keys.ENTER,
|
|
746
|
-
*"secret123", # Environment variable value
|
|
747
|
-
Keys.ENTER,
|
|
748
|
-
Keys.ENTER, # Empty key to finish
|
|
749
|
-
Keys.CTRL_C, # Exit before deployment
|
|
750
|
-
]
|
|
751
|
-
|
|
752
|
-
team = _get_random_team()
|
|
753
|
-
app_data = _get_random_app(team_id=team["id"])
|
|
754
|
-
|
|
755
|
-
respx_mock.get("/teams/").mock(return_value=Response(200, json={"data": [team]}))
|
|
756
|
-
|
|
757
|
-
respx_mock.post("/apps/", json={"name": "demo", "team_id": team["id"]}).mock(
|
|
758
|
-
return_value=Response(201, json=app_data)
|
|
759
|
-
)
|
|
760
|
-
|
|
761
|
-
env_vars_request = respx_mock.patch(
|
|
762
|
-
f"/apps/{app_data['id']}/environment-variables/", json={"API_KEY": "secret123"}
|
|
763
|
-
).mock(return_value=Response(200))
|
|
764
|
-
|
|
765
|
-
with changing_dir(tmp_path), patch(
|
|
766
|
-
"rich_toolkit.container.getchar"
|
|
767
|
-
) as mock_getchar:
|
|
768
|
-
mock_getchar.side_effect = steps
|
|
769
|
-
|
|
770
|
-
result = runner.invoke(app, ["deploy"])
|
|
771
|
-
|
|
772
|
-
assert result.exit_code == 1
|
|
773
|
-
assert env_vars_request.called
|
|
774
|
-
assert "Environment variables set up successfully!" in result.output
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
@pytest.mark.respx(base_url=settings.base_api_url)
|
|
778
|
-
def test_rejects_invalid_environment_variable_names(
|
|
779
|
-
logged_in_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
|
|
780
|
-
) -> None:
|
|
781
|
-
steps = [
|
|
782
|
-
Keys.ENTER, # Setup and deploy
|
|
783
|
-
Keys.ENTER, # Select team
|
|
784
|
-
Keys.ENTER, # Create new app
|
|
785
|
-
*"demo", # App name
|
|
786
|
-
Keys.ENTER,
|
|
787
|
-
Keys.ENTER, # Setup environment variables (Yes)
|
|
788
|
-
*"123-invalid", # Invalid environment variable name (starts with digit, contains hyphen)
|
|
789
|
-
Keys.ENTER,
|
|
790
|
-
*"VALID_KEY", # Valid environment variable name
|
|
791
|
-
Keys.ENTER,
|
|
792
|
-
*"value123", # Environment variable value
|
|
793
|
-
Keys.ENTER,
|
|
794
|
-
Keys.ENTER, # Empty key to finish
|
|
795
|
-
Keys.CTRL_C, # Exit before deployment
|
|
796
|
-
]
|
|
797
|
-
|
|
798
|
-
team = _get_random_team()
|
|
799
|
-
app_data = _get_random_app(team_id=team["id"])
|
|
800
|
-
|
|
801
|
-
respx_mock.get("/teams/").mock(return_value=Response(200, json={"data": [team]}))
|
|
802
|
-
|
|
803
|
-
respx_mock.post("/apps/", json={"name": "demo", "team_id": team["id"]}).mock(
|
|
804
|
-
return_value=Response(201, json=app_data)
|
|
805
|
-
)
|
|
806
|
-
|
|
807
|
-
env_vars_request = respx_mock.patch(
|
|
808
|
-
f"/apps/{app_data['id']}/environment-variables/", json={"VALID_KEY": "value123"}
|
|
809
|
-
).mock(return_value=Response(200))
|
|
810
|
-
|
|
811
|
-
with changing_dir(tmp_path), patch(
|
|
812
|
-
"rich_toolkit.container.getchar"
|
|
813
|
-
) as mock_getchar:
|
|
814
|
-
mock_getchar.side_effect = steps
|
|
815
|
-
|
|
816
|
-
result = runner.invoke(app, ["deploy"])
|
|
817
|
-
|
|
818
|
-
assert result.exit_code == 1
|
|
819
|
-
assert env_vars_request.called
|
|
820
|
-
assert "Invalid environment variable name." in result.output
|
|
821
|
-
assert "Environment variables set up successfully!" in result.output
|
|
822
|
-
|
|
823
|
-
|
|
824
729
|
@pytest.mark.respx(base_url=settings.base_api_url)
|
|
825
730
|
def test_shows_error_for_invalid_waitlist_form_data(
|
|
826
731
|
logged_out_cli: None, tmp_path: Path, respx_mock: respx.MockRouter
|
|
@@ -47,9 +47,7 @@ def test_shows_a_message_if_app_is_not_configured(logged_in_cli: None) -> None:
|
|
|
47
47
|
def test_shows_a_message_if_something_is_wrong(
|
|
48
48
|
logged_in_cli: None, respx_mock: respx.MockRouter, configured_app: Path
|
|
49
49
|
) -> None:
|
|
50
|
-
respx_mock.
|
|
51
|
-
return_value=Response(500)
|
|
52
|
-
)
|
|
50
|
+
respx_mock.post("/apps/123/environment-variables/").mock(return_value=Response(500))
|
|
53
51
|
|
|
54
52
|
with changing_dir(configured_app):
|
|
55
53
|
result = runner.invoke(app, ["env", "set", "SOME_VAR", "secret"])
|
|
@@ -65,9 +63,7 @@ def test_shows_a_message_if_something_is_wrong(
|
|
|
65
63
|
def test_shows_message_when_it_sets(
|
|
66
64
|
logged_in_cli: None, respx_mock: respx.MockRouter, configured_app: Path
|
|
67
65
|
) -> None:
|
|
68
|
-
respx_mock.
|
|
69
|
-
return_value=Response(200)
|
|
70
|
-
)
|
|
66
|
+
respx_mock.post("/apps/123/environment-variables/").mock(return_value=Response(200))
|
|
71
67
|
|
|
72
68
|
with changing_dir(configured_app):
|
|
73
69
|
result = runner.invoke(app, ["env", "set", "SOME_VAR", "secret"])
|
|
@@ -82,9 +78,7 @@ def test_asks_for_name_and_value(
|
|
|
82
78
|
) -> None:
|
|
83
79
|
steps = [*"SOME_VAR", Keys.ENTER, *"secret", Keys.ENTER]
|
|
84
80
|
|
|
85
|
-
respx_mock.
|
|
86
|
-
return_value=Response(200)
|
|
87
|
-
)
|
|
81
|
+
respx_mock.post("/apps/123/environment-variables/").mock(return_value=Response(200))
|
|
88
82
|
|
|
89
83
|
with changing_dir(configured_app), patch(
|
|
90
84
|
"rich_toolkit.container.getchar", side_effect=steps
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/__init__.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/logout.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/unlink.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/src/fastapi_cloud_cli/commands/whoami.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/broken_package/mod/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_api/api.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app/api.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_app/app.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/api.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/app.py
RENAMED
|
File without changes
|
{fastapi_cloud_cli-0.2.1 → fastapi_cloud_cli-0.3.1}/tests/assets/default_files/default_main/main.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|