fastapi-cloud-cli 0.20.0__tar.gz → 0.22.0__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.
Files changed (102) hide show
  1. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/PKG-INFO +1 -4
  2. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/README.md +0 -3
  3. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/pyproject.toml +8 -4
  4. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/__init__.py +1 -0
  5. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/cli.py +4 -0
  6. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/__init__.py +2 -0
  7. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/create.py +15 -2
  8. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/apps/update.py +122 -0
  9. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/ci/__init__.py +15 -0
  10. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/ci/print_workflow.py +45 -0
  11. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/command.py +1 -1
  12. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/upload.py +4 -1
  13. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/wait.py +2 -5
  14. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deployments.py +3 -6
  15. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/logout.py +30 -0
  16. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/logs.py +10 -1
  17. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/setup_ci.py +536 -0
  18. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/tokens/__init__.py +15 -0
  19. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/tokens/create.py +182 -0
  20. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/tokens/delete.py +102 -0
  21. fastapi_cloud_cli-0.22.0/src/fastapi_cloud_cli/commands/tokens/list.py +115 -0
  22. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/api.py +21 -8
  23. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/cli.py +4 -1
  24. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/errors.py +1 -0
  25. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_api_client.py +15 -22
  26. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli.py +58 -1
  27. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_apps.py +165 -0
  28. fastapi_cloud_cli-0.22.0/tests/test_cli_ci.py +297 -0
  29. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_deploy.py +126 -66
  30. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_deployments.py +5 -5
  31. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_logout.py +23 -0
  32. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_setup_ci.py +177 -17
  33. fastapi_cloud_cli-0.22.0/tests/test_cli_tokens.py +702 -0
  34. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_logs.py +38 -0
  35. fastapi_cloud_cli-0.20.0/src/fastapi_cloud_cli/__init__.py +0 -1
  36. fastapi_cloud_cli-0.20.0/src/fastapi_cloud_cli/commands/logout.py +0 -15
  37. fastapi_cloud_cli-0.20.0/src/fastapi_cloud_cli/commands/setup_ci.py +0 -366
  38. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/LICENSE +0 -0
  39. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/add_latest_release_date.py +0 -0
  40. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/format.sh +0 -0
  41. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/lint.sh +0 -0
  42. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/prepare_release.py +0 -0
  43. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/test-cov-html.sh +0 -0
  44. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/scripts/test.sh +0 -0
  45. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/__main__.py +0 -0
  46. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/__init__.py +0 -0
  47. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/_flow.py +0 -0
  48. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/get.py +0 -0
  49. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/link.py +0 -0
  50. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/list.py +0 -0
  51. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/apps/unlink.py +0 -0
  52. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/auth/__init__.py +0 -0
  53. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/auth/wait.py +0 -0
  54. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/__init__.py +0 -0
  55. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/archive.py +0 -0
  56. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/cloud.py +0 -0
  57. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/deploy/configure.py +0 -0
  58. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/__init__.py +0 -0
  59. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/_shared.py +0 -0
  60. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/delete.py +0 -0
  61. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/get.py +0 -0
  62. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/list.py +0 -0
  63. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/env/set.py +0 -0
  64. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/login.py +0 -0
  65. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/teams/__init__.py +0 -0
  66. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/teams/get.py +0 -0
  67. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/commands/whoami.py +0 -0
  68. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/config.py +0 -0
  69. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/logging.py +0 -0
  70. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/py.typed +0 -0
  71. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/__init__.py +0 -0
  72. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/apps.py +0 -0
  73. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/auth.py +0 -0
  74. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/config.py +0 -0
  75. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/dates.py +0 -0
  76. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/env.py +0 -0
  77. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/execution.py +0 -0
  78. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/progress_file.py +0 -0
  79. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/sentry.py +0 -0
  80. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/src/fastapi_cloud_cli/utils/version_check.py +0 -0
  81. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/__init__.py +0 -0
  82. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/conftest.py +0 -0
  83. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_archive.py +0 -0
  84. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_auth.py +0 -0
  85. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_link.py +0 -0
  86. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_login.py +0 -0
  87. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_teams.py +0 -0
  88. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_unlink.py +0 -0
  89. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_cli_whoami.py +0 -0
  90. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_config.py +0 -0
  91. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_dates.py +0 -0
  92. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_deploy_utils.py +0 -0
  93. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_env_delete.py +0 -0
  94. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_env_get.py +0 -0
  95. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_env_list.py +0 -0
  96. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_env_set.py +0 -0
  97. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_prepare_release.py +0 -0
  98. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_progress_file.py +0 -0
  99. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_sentry.py +0 -0
  100. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_utils_apps.py +0 -0
  101. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/test_version_check.py +0 -0
  102. {fastapi_cloud_cli-0.20.0 → fastapi_cloud_cli-0.22.0}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastapi-cloud-cli
3
- Version: 0.20.0
3
+ Version: 0.22.0
4
4
  Summary: Deploy and manage FastAPI Cloud apps from the command line 🚀
5
5
  Author-Email: Patrick Arminio <patrick@fastapilabs.com>
6
6
  License: MIT
@@ -49,9 +49,6 @@ Description-Content-Type: text/markdown
49
49
  <a href="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/test.yml" target="_blank">
50
50
  <img src="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/test.yml/badge.svg" alt="Test">
51
51
  </a>
52
- <a href="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/publish.yml" target="_blank">
53
- <img src="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/publish.yml/badge.svg" alt="Publish">
54
- </a>
55
52
  <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapilabs/fastapi-cloud-cli" target="_blank">
56
53
  <img src="https://coverage-badge.samuelcolvin.workers.dev/fastapilabs/fastapi-cloud-cli.svg" alt="Coverage">
57
54
  <a href="https://pypi.org/project/fastapi-cloud-cli" target="_blank">
@@ -3,9 +3,6 @@
3
3
  <a href="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/test.yml" target="_blank">
4
4
  <img src="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/test.yml/badge.svg" alt="Test">
5
5
  </a>
6
- <a href="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/publish.yml" target="_blank">
7
- <img src="https://github.com/fastapilabs/fastapi-cloud-cli/actions/workflows/publish.yml/badge.svg" alt="Publish">
8
- </a>
9
6
  <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/fastapilabs/fastapi-cloud-cli" target="_blank">
10
7
  <img src="https://coverage-badge.samuelcolvin.workers.dev/fastapilabs/fastapi-cloud-cli.svg" alt="Coverage">
11
8
  <a href="https://pypi.org/project/fastapi-cloud-cli" target="_blank">
@@ -41,7 +41,7 @@ dependencies = [
41
41
  "fastar >= 0.10.0",
42
42
  "detect-installer>=0.1.0",
43
43
  ]
44
- version = "0.20.0"
44
+ version = "0.22.0"
45
45
 
46
46
  [project.license]
47
47
  text = "MIT"
@@ -64,9 +64,9 @@ dev = [
64
64
  "pytest>=7.0.0,<10.0.0",
65
65
  "coverage[toml]>=7.2,<8.0",
66
66
  "mypy==2.1.0",
67
- "ruff==0.15.15",
67
+ "ruff>=0.15.16",
68
68
  "respx==0.23.1",
69
- "time-machine==2.19.0",
69
+ "time-machine>=2.19.0",
70
70
  "ty>=0.0.25",
71
71
  "zizmor>=1.24.1",
72
72
  ]
@@ -170,11 +170,15 @@ keep-runtime-typing = true
170
170
 
171
171
  [tool.typos.files]
172
172
  extend-exclude = [
173
- "release-notes.md",
174
173
  "uv.lock",
175
174
  "coverage/",
176
175
  "htmlcov/",
177
176
  ]
178
177
 
178
+ [tool.typos.default]
179
+ extend-ignore-re = [
180
+ "@[a-zA-Z0-9](?:-?[a-zA-Z0-9])*",
181
+ ]
182
+
179
183
  [tool.typos.default.extend-identifiers]
180
184
  alls = "alls"
@@ -0,0 +1 @@
1
+ __version__ = "0.22.0"
@@ -8,6 +8,7 @@ from .commands.apps import apps_app
8
8
  from .commands.apps.link import link_app
9
9
  from .commands.apps.unlink import unlink_app
10
10
  from .commands.auth import auth_app
11
+ from .commands.ci import ci_app
11
12
  from .commands.deploy import deploy
12
13
  from .commands.deployments import deployments_app
13
14
  from .commands.env import env_app
@@ -16,6 +17,7 @@ from .commands.logout import logout
16
17
  from .commands.logs import logs
17
18
  from .commands.setup_ci import setup_ci
18
19
  from .commands.teams import teams_app
20
+ from .commands.tokens import tokens_app
19
21
  from .commands.whoami import whoami
20
22
  from .logging import setup_logging
21
23
  from .utils.sentry import init_sentry
@@ -69,8 +71,10 @@ cloud_app.command()(setup_ci)
69
71
  cloud_app.add_typer(env_app, name="env")
70
72
  cloud_app.add_typer(auth_app, name="auth")
71
73
  cloud_app.add_typer(apps_app, name="apps")
74
+ cloud_app.add_typer(ci_app, name="ci")
72
75
  cloud_app.add_typer(deployments_app, name="deployments")
73
76
  cloud_app.add_typer(teams_app, name="teams")
77
+ cloud_app.add_typer(tokens_app, name="tokens")
74
78
 
75
79
  # fastapi [command]
76
80
  app.command()(deploy)
@@ -5,6 +5,7 @@ from fastapi_cloud_cli.commands.apps.get import get_app
5
5
  from fastapi_cloud_cli.commands.apps.link import link_app
6
6
  from fastapi_cloud_cli.commands.apps.list import list_apps
7
7
  from fastapi_cloud_cli.commands.apps.unlink import unlink_app
8
+ from fastapi_cloud_cli.commands.apps.update import update_app
8
9
  from fastapi_cloud_cli.commands.logs import logs
9
10
 
10
11
  apps_app = typer.Typer(
@@ -17,5 +18,6 @@ apps_app.command("link")(link_app)
17
18
  apps_app.command("list")(list_apps)
18
19
  apps_app.command("logs")(logs)
19
20
  apps_app.command("unlink")(unlink_app)
21
+ apps_app.command("update")(update_app)
20
22
 
21
23
  __all__ = ["apps_app"]
@@ -75,7 +75,10 @@ def create_app(
75
75
  str | None,
76
76
  typer.Option(
77
77
  "--directory",
78
- help="Directory containing the app's pyproject.toml.",
78
+ help=(
79
+ "Relative app directory containing the pyproject.toml "
80
+ "(for example: backend or webserver)."
81
+ ),
79
82
  ),
80
83
  ] = None,
81
84
  link: Annotated[
@@ -146,7 +149,17 @@ def create_app(
146
149
  )
147
150
  toolkit.print_line()
148
151
 
149
- directory = validate_app_directory(directory)
152
+ try:
153
+ directory = validate_app_directory(directory)
154
+ except ValueError as e:
155
+ toolkit.fail(
156
+ "invalid_input",
157
+ f"Invalid app directory: {e}",
158
+ hint=(
159
+ "Pass a relative app directory such as `backend` or `webserver`; "
160
+ "use --path with --link to choose a local filesystem path."
161
+ ),
162
+ )
150
163
 
151
164
  with toolkit.progress(
152
165
  title="Creating app",
@@ -0,0 +1,122 @@
1
+ import logging
2
+ from typing import Annotated, Any
3
+
4
+ import typer
5
+ from pydantic import BaseModel
6
+ from rich_toolkit import RichToolkit
7
+
8
+ from fastapi_cloud_cli.commands.deploy.archive import validate_app_directory
9
+ from fastapi_cloud_cli.utils.api import APIClient
10
+ from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail
11
+ from fastapi_cloud_cli.utils.auth import Identity
12
+ from fastapi_cloud_cli.utils.cli import get_rich_toolkit
13
+ from fastapi_cloud_cli.utils.execution import JsonOutputOption
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class UpdatedApp(BaseModel):
19
+ id: str
20
+ team_id: str
21
+ slug: str
22
+ name: str
23
+ directory: str | None
24
+
25
+
26
+ class AppsUpdateOutput(BaseModel):
27
+ app: UpdatedApp
28
+
29
+
30
+ def _update_app(client: APIClient, *, app_id: str, directory: str | None) -> UpdatedApp:
31
+ response = client.patch(
32
+ f"/apps/{app_id}",
33
+ json={"directory": directory},
34
+ )
35
+ response.raise_for_status()
36
+
37
+ return UpdatedApp.model_validate(response.json())
38
+
39
+
40
+ def _render_apps_update_output(data: AppsUpdateOutput, toolkit: RichToolkit) -> None:
41
+ toolkit.print(f"Updated app [bold]{data.app.name}[/bold]", bullet=False)
42
+ toolkit.print(
43
+ f"Directory: [bold]{data.app.directory if data.app.directory is not None else '.'}[/bold]",
44
+ bullet=False,
45
+ )
46
+
47
+
48
+ def update_app(
49
+ app_id: Annotated[
50
+ str | None,
51
+ typer.Argument(
52
+ help="ID of the app to update (defaults to the app linked to the current directory).",
53
+ ),
54
+ ] = None,
55
+ directory: Annotated[
56
+ str | None,
57
+ typer.Option(
58
+ "--directory",
59
+ help=(
60
+ "Relative app directory containing the pyproject.toml "
61
+ "(for example: src or backend)."
62
+ ),
63
+ ),
64
+ ] = None,
65
+ json_output: JsonOutputOption = False,
66
+ ) -> Any:
67
+ """
68
+ Update FastAPI Cloud app metadata.
69
+ """
70
+ identity = Identity()
71
+
72
+ with get_rich_toolkit(json_output=json_output) as toolkit:
73
+ if not identity.is_logged_in():
74
+ toolkit.fail(
75
+ "not_logged_in",
76
+ "No credentials found.",
77
+ hint="Run `fastapi cloud login` or set FASTAPI_CLOUD_TOKEN.",
78
+ )
79
+
80
+ if directory is None:
81
+ toolkit.fail(
82
+ "missing_required_input",
83
+ "No updates provided.",
84
+ hint="Pass --directory to update the app directory.",
85
+ )
86
+
87
+ target_app_id = resolve_app_id_or_fail(
88
+ toolkit,
89
+ app_id=app_id,
90
+ hint="Pass an app ID or run `fastapi cloud apps create --link` first.",
91
+ )
92
+
93
+ try:
94
+ directory = validate_app_directory(directory)
95
+ except ValueError as e:
96
+ toolkit.fail(
97
+ "invalid_input",
98
+ f"Invalid app directory: {e}",
99
+ hint="Pass a relative app directory such as `src` or `backend`.",
100
+ )
101
+
102
+ with APIClient() as client:
103
+ with toolkit.progress(
104
+ title="Updating app",
105
+ transient=True,
106
+ ) as progress:
107
+ with client.handle_http_errors(
108
+ progress,
109
+ default_message="Error updating app. Please try again later.",
110
+ not_found_message="App not found.",
111
+ toolkit=toolkit,
112
+ ):
113
+ app = _update_app(
114
+ client,
115
+ app_id=target_app_id,
116
+ directory=directory,
117
+ )
118
+
119
+ toolkit.success(
120
+ AppsUpdateOutput(app=app),
121
+ render_output=_render_apps_update_output,
122
+ )
@@ -0,0 +1,15 @@
1
+ import typer
2
+
3
+ from fastapi_cloud_cli.commands.ci.print_workflow import (
4
+ print_workflow as print_workflow_command,
5
+ )
6
+ from fastapi_cloud_cli.commands.setup_ci import setup_ci
7
+
8
+ ci_app = typer.Typer(
9
+ no_args_is_help=True,
10
+ help="Manage CI integration helpers.",
11
+ )
12
+ ci_app.command("print-workflow")(print_workflow_command)
13
+ ci_app.command("setup")(setup_ci)
14
+
15
+ __all__ = ["ci_app"]
@@ -0,0 +1,45 @@
1
+ from typing import Annotated, Any
2
+
3
+ import typer
4
+ from pydantic import BaseModel
5
+ from rich_toolkit import RichToolkit
6
+
7
+ from fastapi_cloud_cli.commands.setup_ci import (
8
+ DEFAULT_WORKFLOW_PATH,
9
+ _get_default_branch,
10
+ _get_workflow_content,
11
+ )
12
+ from fastapi_cloud_cli.utils.cli import get_rich_toolkit
13
+ from fastapi_cloud_cli.utils.execution import JsonOutputOption
14
+
15
+
16
+ class CIWorkflowOutput(BaseModel):
17
+ filename: str
18
+ content: str
19
+
20
+
21
+ def _render_workflow_output(data: CIWorkflowOutput, toolkit: RichToolkit) -> None:
22
+ toolkit.console.print(data.content, markup=False, end="")
23
+
24
+
25
+ def print_workflow(
26
+ branch: Annotated[
27
+ str | None,
28
+ typer.Option(
29
+ "--branch",
30
+ "-b",
31
+ help="Branch that triggers deploys (defaults to the repo's default branch).",
32
+ ),
33
+ ] = None,
34
+ json_output: JsonOutputOption = False,
35
+ ) -> Any:
36
+ """Prints the GitHub Actions workflow YAML without writing files or secrets."""
37
+
38
+ branch = branch or _get_default_branch()
39
+ workflow = CIWorkflowOutput(
40
+ filename=DEFAULT_WORKFLOW_PATH.name,
41
+ content=_get_workflow_content(branch),
42
+ )
43
+
44
+ with get_rich_toolkit(minimal=True, json_output=json_output) as toolkit:
45
+ toolkit.success(workflow, render_output=_render_workflow_output)
@@ -331,7 +331,7 @@ def deploy(
331
331
  f"Deployment created successfully! Deployment slug: {deployment.slug}"
332
332
  )
333
333
 
334
- _upload_deployment(
334
+ deployment = _upload_deployment(
335
335
  fastapi_client=client,
336
336
  deployment_id=deployment.id,
337
337
  archive_path=archive_path,
@@ -6,6 +6,7 @@ from httpx import Client
6
6
  from pydantic import BaseModel
7
7
  from rich_toolkit.progress import Progress
8
8
 
9
+ from fastapi_cloud_cli.commands.deploy.cloud import CreateDeploymentResponse
9
10
  from fastapi_cloud_cli.utils.api import APIClient
10
11
  from fastapi_cloud_cli.utils.progress_file import ProgressFile
11
12
 
@@ -43,7 +44,7 @@ def _upload_deployment(
43
44
  deployment_id: str,
44
45
  archive_path: Path,
45
46
  progress: Progress,
46
- ) -> None:
47
+ ) -> CreateDeploymentResponse:
47
48
  archive_size = archive_path.stat().st_size
48
49
  archive_size_str = _format_size(archive_size)
49
50
 
@@ -88,3 +89,5 @@ def _upload_deployment(
88
89
 
89
90
  notify_response.raise_for_status()
90
91
  logger.debug("Upload notification sent successfully")
92
+
93
+ return CreateDeploymentResponse.model_validate(notify_response.json())
@@ -46,7 +46,6 @@ LONG_WAIT_MESSAGES = [
46
46
  def _verify_deployment(
47
47
  toolkit: RichToolkit,
48
48
  client: APIClient,
49
- app_id: str,
50
49
  deployment: CreateDeploymentResponse,
51
50
  ) -> None:
52
51
  failed_status: str | None = None
@@ -57,7 +56,7 @@ def _verify_deployment(
57
56
  done_emoji="✅",
58
57
  ) as progress:
59
58
  try:
60
- final_status = client.poll_deployment_status(app_id, deployment.id)
59
+ final_status = client.poll_deployment_status(deployment.id)
61
60
  except (TimeoutError, TooManyRetriesError, StreamLogError):
62
61
  progress.metadata["done_emoji"] = "⚠️"
63
62
  progress.current_message = (
@@ -168,6 +167,4 @@ def _wait_for_deployment(
168
167
  if build_complete:
169
168
  toolkit.print_line()
170
169
 
171
- _verify_deployment(
172
- toolkit=toolkit, client=client, app_id=app_id, deployment=deployment
173
- )
170
+ _verify_deployment(toolkit=toolkit, client=client, deployment=deployment)
@@ -95,10 +95,8 @@ def _get_deployments(
95
95
  )
96
96
 
97
97
 
98
- def _get_deployment(
99
- client: APIClient, *, app_id: str, deployment_id: str
100
- ) -> DeploymentGetOutput:
101
- response = client.get(f"/apps/{app_id}/deployments/{deployment_id}")
98
+ def _get_deployment(client: APIClient, *, deployment_id: str) -> DeploymentGetOutput:
99
+ response = client.get(f"/deployments/{deployment_id}")
102
100
  response.raise_for_status()
103
101
 
104
102
  return DeploymentGetOutput(deployment=Deployment.model_validate(response.json()))
@@ -344,7 +342,7 @@ def get_deployment(
344
342
  hint="Run `fastapi cloud login` or set FASTAPI_CLOUD_TOKEN.",
345
343
  )
346
344
 
347
- target_app_id = resolve_app_id_or_fail(toolkit, app_id=app_id)
345
+ resolve_app_id_or_fail(toolkit, app_id=app_id)
348
346
 
349
347
  with APIClient() as client:
350
348
  with toolkit.progress(
@@ -359,7 +357,6 @@ def get_deployment(
359
357
  ):
360
358
  result = _get_deployment(
361
359
  client,
362
- app_id=target_app_id,
363
360
  deployment_id=deployment_id,
364
361
  )
365
362
 
@@ -0,0 +1,30 @@
1
+ from typing import Any
2
+
3
+ from pydantic import BaseModel
4
+ from rich_toolkit import RichToolkit
5
+
6
+ from fastapi_cloud_cli.utils.auth import delete_auth_config
7
+ from fastapi_cloud_cli.utils.cli import get_rich_toolkit
8
+ from fastapi_cloud_cli.utils.execution import JsonOutputOption
9
+
10
+
11
+ class LogoutOutput(BaseModel):
12
+ logged_out: bool
13
+
14
+
15
+ def _render_logout_output(data: LogoutOutput, toolkit: RichToolkit) -> None:
16
+ toolkit.print_title("FastAPI Cloud")
17
+ toolkit.print_line()
18
+ toolkit.print("You are now logged out!", emoji="👋")
19
+
20
+
21
+ def logout(json_output: JsonOutputOption = False) -> Any:
22
+ """
23
+ Logout from FastAPI Cloud.
24
+ """
25
+ with get_rich_toolkit(json_output=json_output) as toolkit:
26
+ delete_auth_config()
27
+ toolkit.success(
28
+ LogoutOutput(logged_out=True),
29
+ render_output=_render_logout_output,
30
+ )
@@ -131,6 +131,12 @@ def _handle_stream_log_error(
131
131
  message = handle_http_error(error.__cause__)
132
132
  hint = get_http_error_hint(code)
133
133
 
134
+ if error.status_code == 400 and hint is None:
135
+ hint = (
136
+ "Try a shorter time range (e.g. `--since 1d`). "
137
+ "Log retention depends on your plan."
138
+ )
139
+
134
140
  else:
135
141
  code = "api_error"
136
142
  message = f"[red]Error:[/] {escape(str(error))}"
@@ -215,7 +221,10 @@ def logs(
215
221
  "5m",
216
222
  "--since",
217
223
  "-s",
218
- help="Show logs since a specific time (e.g., '5m', '1h', '2d').",
224
+ help=(
225
+ "Show logs since a specific time (e.g., '5m', '1h', '2d'). "
226
+ "Limited by your plan's log retention."
227
+ ),
219
228
  show_default=True,
220
229
  callback=_validate_since,
221
230
  ),