fastapi-cloud-cli 0.25.0__py3-none-any.whl → 0.26.0__py3-none-any.whl
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/__init__.py +1 -1
- fastapi_cloud_cli/api/__init__.py +9 -0
- fastapi_cloud_cli/api/_models.py +41 -5
- fastapi_cloud_cli/api/client.py +36 -0
- fastapi_cloud_cli/commands/deploy/wait.py +25 -4
- fastapi_cloud_cli/commands/deployments.py +99 -59
- fastapi_cloud_cli/commands/env/_shared.py +1 -22
- fastapi_cloud_cli/commands/env/delete.py +19 -31
- fastapi_cloud_cli/commands/env/get.py +3 -5
- fastapi_cloud_cli/commands/env/list.py +3 -5
- fastapi_cloud_cli/commands/env/set.py +24 -20
- fastapi_cloud_cli/utils/build_logs.py +14 -0
- fastapi_cloud_cli/utils/cli.py +5 -1
- {fastapi_cloud_cli-0.25.0.dist-info → fastapi_cloud_cli-0.26.0.dist-info}/METADATA +2 -2
- {fastapi_cloud_cli-0.25.0.dist-info → fastapi_cloud_cli-0.26.0.dist-info}/RECORD +18 -17
- {fastapi_cloud_cli-0.25.0.dist-info → fastapi_cloud_cli-0.26.0.dist-info}/WHEEL +0 -0
- {fastapi_cloud_cli-0.25.0.dist-info → fastapi_cloud_cli-0.26.0.dist-info}/entry_points.txt +0 -0
- {fastapi_cloud_cli-0.25.0.dist-info → fastapi_cloud_cli-0.26.0.dist-info}/licenses/LICENSE +0 -0
fastapi_cloud_cli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.26.0"
|
|
@@ -4,12 +4,21 @@ from ._errors import _get_response_error_message as _get_response_error_message
|
|
|
4
4
|
from ._errors import get_http_error_code as get_http_error_code
|
|
5
5
|
from ._errors import get_http_error_hint as get_http_error_hint
|
|
6
6
|
from ._errors import handle_http_error as handle_http_error
|
|
7
|
+
from ._models import BUILD_FAILED_STATUSES as BUILD_FAILED_STATUSES
|
|
7
8
|
from ._models import SUCCESSFUL_STATUSES as SUCCESSFUL_STATUSES
|
|
9
|
+
from ._models import TERMINAL_STATUSES as TERMINAL_STATUSES
|
|
8
10
|
from ._models import AppLogEntry as AppLogEntry
|
|
11
|
+
from ._models import BuildFailure as BuildFailure
|
|
12
|
+
from ._models import BuildLogLineGeneric as BuildLogLineGeneric
|
|
9
13
|
from ._models import BuildLogLineMessage as BuildLogLineMessage
|
|
10
14
|
from ._models import CustomDomain as CustomDomain
|
|
11
15
|
from ._models import CustomDomainRecord as CustomDomainRecord
|
|
12
16
|
from ._models import CustomDomainStatus as CustomDomainStatus
|
|
17
|
+
from ._models import Deployment as Deployment
|
|
13
18
|
from ._models import DeploymentStatus as DeploymentStatus
|
|
19
|
+
from ._models import EnvironmentVariable as EnvironmentVariable
|
|
20
|
+
from ._models import (
|
|
21
|
+
EnvironmentVariableCreatePayload as EnvironmentVariableCreatePayload,
|
|
22
|
+
)
|
|
14
23
|
from ._retry import STREAM_LOGS_MAX_RETRIES as STREAM_LOGS_MAX_RETRIES
|
|
15
24
|
from .client import APIClient as APIClient
|
fastapi_cloud_cli/api/_models.py
CHANGED
|
@@ -4,6 +4,22 @@ from typing import Annotated, Literal
|
|
|
4
4
|
from pydantic import BaseModel, Field, TypeAdapter
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
class EnvironmentVariable(BaseModel):
|
|
8
|
+
name: str
|
|
9
|
+
value: str | None = None
|
|
10
|
+
is_secret: bool = False
|
|
11
|
+
updated_at: str | None = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EnvironmentVariableResponse(BaseModel):
|
|
15
|
+
data: list[EnvironmentVariable]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EnvironmentVariableCreatePayload(BaseModel):
|
|
19
|
+
value: str
|
|
20
|
+
is_secret: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
7
23
|
class AppLogEntry(BaseModel):
|
|
8
24
|
timestamp: str
|
|
9
25
|
message: str
|
|
@@ -21,6 +37,13 @@ class BuildLogLineMessage(BaseModel):
|
|
|
21
37
|
id: str | None = None
|
|
22
38
|
|
|
23
39
|
|
|
40
|
+
class BuildFailure(BaseModel):
|
|
41
|
+
error_code: str
|
|
42
|
+
error_title: str
|
|
43
|
+
error_message: str
|
|
44
|
+
error_hint: str
|
|
45
|
+
|
|
46
|
+
|
|
24
47
|
BuildLogLine = BuildLogLineMessage | BuildLogLineGeneric
|
|
25
48
|
BuildLogAdapter: TypeAdapter[BuildLogLine] = TypeAdapter(
|
|
26
49
|
Annotated[BuildLogLine, Field(discriminator="type")]
|
|
@@ -125,16 +148,29 @@ class DeploymentStatus(str, Enum):
|
|
|
125
148
|
}[status]
|
|
126
149
|
|
|
127
150
|
|
|
151
|
+
class Deployment(BaseModel):
|
|
152
|
+
id: str
|
|
153
|
+
app_id: str
|
|
154
|
+
slug: str
|
|
155
|
+
status: DeploymentStatus
|
|
156
|
+
created_at: str
|
|
157
|
+
url: str | None = None
|
|
158
|
+
dashboard_url: str | None = None
|
|
159
|
+
failure: BuildFailure | None = None
|
|
160
|
+
|
|
161
|
+
|
|
128
162
|
SUCCESSFUL_STATUSES = {DeploymentStatus.success, DeploymentStatus.verifying_skipped}
|
|
129
|
-
|
|
163
|
+
BUILD_FAILED_STATUSES = {
|
|
164
|
+
DeploymentStatus.building_image_failed,
|
|
165
|
+
DeploymentStatus.building_image_failed_timeout,
|
|
166
|
+
DeploymentStatus.extracting_failed,
|
|
167
|
+
DeploymentStatus.extracting_failed_archive_too_large,
|
|
168
|
+
}
|
|
169
|
+
FAILED_STATUSES = BUILD_FAILED_STATUSES | {
|
|
130
170
|
DeploymentStatus.failed,
|
|
131
171
|
DeploymentStatus.verifying_failed,
|
|
132
172
|
DeploymentStatus.verification_failed_oom,
|
|
133
173
|
DeploymentStatus.deploying_failed,
|
|
134
174
|
DeploymentStatus.deploying_skipped,
|
|
135
|
-
DeploymentStatus.building_image_failed,
|
|
136
|
-
DeploymentStatus.building_image_failed_timeout,
|
|
137
|
-
DeploymentStatus.extracting_failed,
|
|
138
|
-
DeploymentStatus.extracting_failed_archive_too_large,
|
|
139
175
|
}
|
|
140
176
|
TERMINAL_STATUSES = SUCCESSFUL_STATUSES | FAILED_STATUSES
|
fastapi_cloud_cli/api/client.py
CHANGED
|
@@ -30,7 +30,10 @@ from ._models import (
|
|
|
30
30
|
BuildLogLine,
|
|
31
31
|
CustomDomain,
|
|
32
32
|
CustomDomainsAPIResponse,
|
|
33
|
+
Deployment,
|
|
33
34
|
DeploymentStatus,
|
|
35
|
+
EnvironmentVariableCreatePayload,
|
|
36
|
+
EnvironmentVariableResponse,
|
|
34
37
|
)
|
|
35
38
|
from ._retry import (
|
|
36
39
|
STREAM_LOGS_MAX_RETRIES,
|
|
@@ -136,6 +139,39 @@ class APIClient(httpx.Client):
|
|
|
136
139
|
|
|
137
140
|
raise typer.Exit(1) from None
|
|
138
141
|
|
|
142
|
+
def get_deployment(self, deployment_id: str) -> Deployment:
|
|
143
|
+
response = self.get(f"/deployments/{deployment_id}")
|
|
144
|
+
response.raise_for_status()
|
|
145
|
+
return Deployment.model_validate(response.json())
|
|
146
|
+
|
|
147
|
+
def get_environment_variables(self, *, app_id: str) -> EnvironmentVariableResponse:
|
|
148
|
+
response = self.get(f"/apps/{app_id}/environment-variables/")
|
|
149
|
+
response.raise_for_status()
|
|
150
|
+
|
|
151
|
+
return EnvironmentVariableResponse.model_validate(response.json())
|
|
152
|
+
|
|
153
|
+
def batch_environment_variables(
|
|
154
|
+
self,
|
|
155
|
+
*,
|
|
156
|
+
app_id: str,
|
|
157
|
+
upsert: dict[str, EnvironmentVariableCreatePayload],
|
|
158
|
+
delete: list[str],
|
|
159
|
+
redeploy: bool = True,
|
|
160
|
+
) -> EnvironmentVariableResponse:
|
|
161
|
+
response = self.put(
|
|
162
|
+
f"/apps/{app_id}/environment-variables/",
|
|
163
|
+
json={
|
|
164
|
+
"upsert": {
|
|
165
|
+
name: payload.model_dump() for name, payload in upsert.items()
|
|
166
|
+
},
|
|
167
|
+
"delete": delete,
|
|
168
|
+
"redeploy": redeploy,
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
response.raise_for_status()
|
|
172
|
+
|
|
173
|
+
return EnvironmentVariableResponse.model_validate(response.json())
|
|
174
|
+
|
|
139
175
|
def get_custom_domains(self, *, app_id: str) -> CustomDomainsAPIResponse:
|
|
140
176
|
response = self.get(f"/apps/{app_id}/custom-domains")
|
|
141
177
|
response.raise_for_status()
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import time
|
|
2
3
|
from itertools import cycle
|
|
3
4
|
from textwrap import dedent
|
|
4
5
|
|
|
5
6
|
import typer
|
|
7
|
+
from httpx import HTTPError
|
|
6
8
|
from rich.text import Text
|
|
7
9
|
from rich_toolkit import RichToolkit
|
|
8
10
|
|
|
9
11
|
from fastapi_cloud_cli.api import (
|
|
10
12
|
SUCCESSFUL_STATUSES,
|
|
11
13
|
APIClient,
|
|
14
|
+
BuildFailure,
|
|
12
15
|
DeploymentStatus,
|
|
13
16
|
StreamLogError,
|
|
14
17
|
TooManyRetriesError,
|
|
15
18
|
)
|
|
16
19
|
from fastapi_cloud_cli.commands.deploy.cloud import CreateDeploymentResponse
|
|
20
|
+
from fastapi_cloud_cli.utils.build_logs import print_build_error
|
|
21
|
+
from fastapi_cloud_cli.utils.cli import FastAPIRichToolkit
|
|
22
|
+
from fastapi_cloud_cli.utils.execution import is_ci_enabled
|
|
17
23
|
|
|
18
24
|
# (bullet emoji, message) — the emoji replaces the progress animation
|
|
19
25
|
WAITING_MESSAGES = [
|
|
@@ -89,7 +95,7 @@ def _verify_deployment(
|
|
|
89
95
|
|
|
90
96
|
|
|
91
97
|
def _wait_for_deployment(
|
|
92
|
-
toolkit:
|
|
98
|
+
toolkit: FastAPIRichToolkit,
|
|
93
99
|
client: APIClient,
|
|
94
100
|
app_id: str,
|
|
95
101
|
deployment: CreateDeploymentResponse,
|
|
@@ -107,12 +113,15 @@ def _wait_for_deployment(
|
|
|
107
113
|
"Checking the status of your deployment",
|
|
108
114
|
inline_logs=True,
|
|
109
115
|
lines_to_show=20,
|
|
116
|
+
# Keep the log panel replaceable by a diagnosis, but retain CI logs.
|
|
117
|
+
preserve_logs=is_ci_enabled(),
|
|
110
118
|
emoji="👀",
|
|
111
119
|
done_emoji="🚀",
|
|
112
120
|
) as progress,
|
|
113
121
|
):
|
|
114
122
|
build_complete = False
|
|
115
123
|
build_failed = False
|
|
124
|
+
build_error: BuildFailure | None = None
|
|
116
125
|
|
|
117
126
|
try:
|
|
118
127
|
for log in client.stream_build_logs(deployment.id):
|
|
@@ -128,6 +137,12 @@ def _wait_for_deployment(
|
|
|
128
137
|
|
|
129
138
|
if log.type == "failed":
|
|
130
139
|
build_failed = True
|
|
140
|
+
build_error = None
|
|
141
|
+
|
|
142
|
+
with contextlib.suppress(HTTPError):
|
|
143
|
+
build_error = client.get_deployment(deployment.id).failure
|
|
144
|
+
|
|
145
|
+
progress.transient = build_error is not None
|
|
131
146
|
# the headline comes from the title once there are log
|
|
132
147
|
# lines, and from current_message when there are none
|
|
133
148
|
progress.title = "Build failed"
|
|
@@ -157,10 +172,16 @@ def _wait_for_deployment(
|
|
|
157
172
|
raise typer.Exit(1) from None
|
|
158
173
|
|
|
159
174
|
if build_failed:
|
|
160
|
-
|
|
175
|
+
if build_error is not None:
|
|
176
|
+
print_build_error(toolkit, build_error)
|
|
177
|
+
toolkit.print_line()
|
|
178
|
+
message = "Check out the logs at"
|
|
179
|
+
else:
|
|
180
|
+
toolkit.print_line()
|
|
181
|
+
message = "Oh no! Something went wrong. Check out the logs at"
|
|
161
182
|
toolkit.print(
|
|
162
|
-
f"
|
|
163
|
-
emoji="😔",
|
|
183
|
+
f"{message} [link={deployment.dashboard_url}]{deployment.dashboard_url}[/link]",
|
|
184
|
+
emoji="👀" if build_error is not None else "😔",
|
|
164
185
|
)
|
|
165
186
|
raise typer.Exit(1)
|
|
166
187
|
|
|
@@ -5,14 +5,19 @@ from typing import Annotated, Any
|
|
|
5
5
|
import typer
|
|
6
6
|
from httpx import HTTPError
|
|
7
7
|
from pydantic import BaseModel
|
|
8
|
+
from rich.markup import escape
|
|
8
9
|
from rich.table import Table
|
|
9
10
|
from rich.text import Text
|
|
10
11
|
from rich_toolkit import RichToolkit
|
|
11
12
|
|
|
12
13
|
from fastapi_cloud_cli.api import (
|
|
14
|
+
BUILD_FAILED_STATUSES,
|
|
15
|
+
TERMINAL_STATUSES,
|
|
13
16
|
APIClient,
|
|
17
|
+
BuildFailure,
|
|
18
|
+
BuildLogLineGeneric,
|
|
14
19
|
BuildLogLineMessage,
|
|
15
|
-
|
|
20
|
+
Deployment,
|
|
16
21
|
StreamLogError,
|
|
17
22
|
TooManyRetriesError,
|
|
18
23
|
get_http_error_code,
|
|
@@ -35,16 +40,6 @@ DEFAULT_LIMIT = 100
|
|
|
35
40
|
DEFAULT_OFFSET = 0
|
|
36
41
|
|
|
37
42
|
|
|
38
|
-
class Deployment(BaseModel):
|
|
39
|
-
id: str
|
|
40
|
-
app_id: str
|
|
41
|
-
slug: str
|
|
42
|
-
status: DeploymentStatus
|
|
43
|
-
created_at: str
|
|
44
|
-
url: str | None = None
|
|
45
|
-
dashboard_url: str | None = None
|
|
46
|
-
|
|
47
|
-
|
|
48
43
|
class DeploymentsListAPIResponse(BaseModel):
|
|
49
44
|
data: list[Deployment]
|
|
50
45
|
count: int
|
|
@@ -70,6 +65,7 @@ class BuildLogsOutput(BaseModel):
|
|
|
70
65
|
deployment_id: str
|
|
71
66
|
failed: bool
|
|
72
67
|
logs: list[BuildLogOutput]
|
|
68
|
+
failure: BuildFailure | None = None
|
|
73
69
|
|
|
74
70
|
|
|
75
71
|
def _get_deployments(
|
|
@@ -94,13 +90,6 @@ def _get_deployments(
|
|
|
94
90
|
)
|
|
95
91
|
|
|
96
92
|
|
|
97
|
-
def _get_deployment(client: APIClient, *, deployment_id: str) -> DeploymentGetOutput:
|
|
98
|
-
response = client.get(f"/deployments/{deployment_id}")
|
|
99
|
-
response.raise_for_status()
|
|
100
|
-
|
|
101
|
-
return DeploymentGetOutput(deployment=Deployment.model_validate(response.json()))
|
|
102
|
-
|
|
103
|
-
|
|
104
93
|
def _render_deployments_list_output(
|
|
105
94
|
data: DeploymentsListOutput, toolkit: RichToolkit
|
|
106
95
|
) -> None:
|
|
@@ -129,7 +118,7 @@ def _render_deployments_list_output(
|
|
|
129
118
|
|
|
130
119
|
|
|
131
120
|
def _render_deployment_get_output(
|
|
132
|
-
data: DeploymentGetOutput, toolkit:
|
|
121
|
+
data: DeploymentGetOutput, toolkit: FastAPIRichToolkit
|
|
133
122
|
) -> None:
|
|
134
123
|
deployment = data.deployment
|
|
135
124
|
|
|
@@ -164,6 +153,23 @@ def _render_deployment_get_output(
|
|
|
164
153
|
)
|
|
165
154
|
)
|
|
166
155
|
|
|
156
|
+
if deployment.failure is not None:
|
|
157
|
+
toolkit.print_line()
|
|
158
|
+
_print_deployment_failure(toolkit, deployment.failure)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _print_deployment_failure(
|
|
162
|
+
toolkit: FastAPIRichToolkit, failure: BuildFailure
|
|
163
|
+
) -> None:
|
|
164
|
+
toolkit.print("This deployment failed with the following error:", emoji="🚨")
|
|
165
|
+
toolkit.print_line()
|
|
166
|
+
toolkit.print(Text(failure.error_title, style="bold"))
|
|
167
|
+
toolkit.print_line()
|
|
168
|
+
toolkit.print(Text(failure.error_message))
|
|
169
|
+
if failure.error_hint:
|
|
170
|
+
toolkit.print_line()
|
|
171
|
+
toolkit.print_hint(escape(failure.error_hint))
|
|
172
|
+
|
|
167
173
|
|
|
168
174
|
def _print_build_log_json(
|
|
169
175
|
deployment_id: str,
|
|
@@ -171,12 +177,14 @@ def _print_build_log_json(
|
|
|
171
177
|
*,
|
|
172
178
|
log_id: str | None,
|
|
173
179
|
message: str | None = None,
|
|
180
|
+
failure: BuildFailure | None = None,
|
|
174
181
|
) -> None:
|
|
175
182
|
record = {
|
|
176
183
|
"type": record_type,
|
|
177
184
|
"deployment_id": deployment_id,
|
|
178
185
|
"id": log_id,
|
|
179
186
|
"message": message,
|
|
187
|
+
"failure": failure.model_dump(mode="json") if failure is not None else None,
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
typer.echo(
|
|
@@ -191,22 +199,31 @@ BUILD_LOG_BULLET = "[dim]▕[/dim]"
|
|
|
191
199
|
|
|
192
200
|
|
|
193
201
|
def _print_build_log_line(toolkit: RichToolkit, message: str) -> None:
|
|
194
|
-
|
|
202
|
+
text = Text.from_ansi(message.rstrip())
|
|
203
|
+
|
|
204
|
+
if not text.plain.strip():
|
|
205
|
+
# Keep the log marker when the style would otherwise omit an empty line.
|
|
206
|
+
text = Text("\u200b")
|
|
207
|
+
|
|
208
|
+
toolkit.print(text, emoji=BUILD_LOG_BULLET)
|
|
195
209
|
|
|
196
210
|
|
|
197
211
|
def _render_build_logs_output(
|
|
198
212
|
data: BuildLogsOutput, toolkit: FastAPIRichToolkit
|
|
199
213
|
) -> None:
|
|
200
|
-
if not data.logs:
|
|
201
|
-
toolkit.print("No build logs found.")
|
|
202
|
-
return
|
|
203
|
-
|
|
204
214
|
for log in data.logs:
|
|
205
215
|
_print_build_log_line(toolkit, log.message)
|
|
206
216
|
|
|
207
|
-
if data.
|
|
217
|
+
if data.failure:
|
|
218
|
+
if data.logs:
|
|
219
|
+
toolkit.print_line()
|
|
220
|
+
|
|
221
|
+
_print_deployment_failure(toolkit, data.failure)
|
|
222
|
+
elif data.failed:
|
|
208
223
|
toolkit.print_line()
|
|
209
224
|
toolkit.print_error("Build failed.")
|
|
225
|
+
elif not data.logs:
|
|
226
|
+
toolkit.print("No build logs found.")
|
|
210
227
|
|
|
211
228
|
|
|
212
229
|
def _stream_build_logs(
|
|
@@ -214,9 +231,12 @@ def _stream_build_logs(
|
|
|
214
231
|
client: APIClient,
|
|
215
232
|
deployment_id: str,
|
|
216
233
|
) -> bool:
|
|
217
|
-
|
|
234
|
+
deployment = client.get_deployment(deployment_id)
|
|
235
|
+
terminal_log: BuildLogLineGeneric | None = None
|
|
218
236
|
|
|
219
|
-
for log in client.stream_build_logs(
|
|
237
|
+
for log in client.stream_build_logs(
|
|
238
|
+
deployment_id, follow=deployment.status not in TERMINAL_STATUSES
|
|
239
|
+
):
|
|
220
240
|
if isinstance(log, BuildLogLineMessage):
|
|
221
241
|
if toolkit.mode == "json":
|
|
222
242
|
_print_build_log_json(
|
|
@@ -227,26 +247,30 @@ def _stream_build_logs(
|
|
|
227
247
|
)
|
|
228
248
|
else:
|
|
229
249
|
_print_build_log_line(toolkit, log.message)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
+
else:
|
|
251
|
+
terminal_log = log
|
|
252
|
+
|
|
253
|
+
if deployment.status not in TERMINAL_STATUSES:
|
|
254
|
+
deployment = client.get_deployment(deployment_id)
|
|
255
|
+
|
|
256
|
+
failed = (
|
|
257
|
+
terminal_log is not None and terminal_log.type == "failed"
|
|
258
|
+
) or deployment.status in BUILD_FAILED_STATUSES
|
|
259
|
+
|
|
260
|
+
if toolkit.mode == "json":
|
|
261
|
+
if failed or terminal_log is not None or deployment.status in TERMINAL_STATUSES:
|
|
262
|
+
_print_build_log_json(
|
|
263
|
+
deployment_id,
|
|
264
|
+
"failed" if failed else "complete",
|
|
265
|
+
log_id=terminal_log.id if terminal_log is not None else None,
|
|
266
|
+
failure=deployment.failure,
|
|
267
|
+
)
|
|
268
|
+
elif deployment.failure is not None:
|
|
269
|
+
toolkit.print_line()
|
|
270
|
+
_print_deployment_failure(toolkit, deployment.failure)
|
|
271
|
+
elif failed:
|
|
272
|
+
toolkit.print_line()
|
|
273
|
+
toolkit.print_error("Build failed.")
|
|
250
274
|
|
|
251
275
|
return failed
|
|
252
276
|
|
|
@@ -262,7 +286,13 @@ def _fetch_build_logs(client: APIClient, deployment_id: str) -> BuildLogsOutput:
|
|
|
262
286
|
elif log.type == "failed":
|
|
263
287
|
failed = True
|
|
264
288
|
|
|
265
|
-
|
|
289
|
+
deployment = client.get_deployment(deployment_id)
|
|
290
|
+
return BuildLogsOutput(
|
|
291
|
+
deployment_id=deployment_id,
|
|
292
|
+
failed=failed or deployment.status in BUILD_FAILED_STATUSES,
|
|
293
|
+
logs=logs,
|
|
294
|
+
failure=deployment.failure,
|
|
295
|
+
)
|
|
266
296
|
|
|
267
297
|
|
|
268
298
|
def _handle_build_log_error(
|
|
@@ -338,22 +368,24 @@ def get_deployment(
|
|
|
338
368
|
resolve_app_id_or_fail(toolkit, app_id=app_id)
|
|
339
369
|
|
|
340
370
|
with APIClient() as client:
|
|
341
|
-
with
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
371
|
+
with (
|
|
372
|
+
toolkit.progress(
|
|
373
|
+
title="Fetching deployment",
|
|
374
|
+
transient=True,
|
|
375
|
+
) as progress,
|
|
376
|
+
client.handle_http_errors(
|
|
346
377
|
progress,
|
|
347
378
|
default_message="Error fetching deployment. Please try again later.",
|
|
348
379
|
not_found_message="Deployment not found.",
|
|
349
380
|
toolkit=toolkit,
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
deployment_id=deployment_id,
|
|
354
|
-
)
|
|
381
|
+
),
|
|
382
|
+
):
|
|
383
|
+
deployment = client.get_deployment(deployment_id)
|
|
355
384
|
|
|
356
|
-
toolkit.success(
|
|
385
|
+
toolkit.success(
|
|
386
|
+
DeploymentGetOutput(deployment=deployment),
|
|
387
|
+
render_output=_render_deployment_get_output,
|
|
388
|
+
)
|
|
357
389
|
|
|
358
390
|
|
|
359
391
|
@deployments_app.command("build-logs", cls=UserCommand)
|
|
@@ -407,6 +439,14 @@ def build_logs(
|
|
|
407
439
|
return
|
|
408
440
|
except StreamLogError as e:
|
|
409
441
|
_handle_build_log_error(toolkit, e)
|
|
442
|
+
except HTTPError as e:
|
|
443
|
+
code = get_http_error_code(e)
|
|
444
|
+
toolkit.fail(
|
|
445
|
+
code,
|
|
446
|
+
handle_http_error(e, not_found_message="Deployment not found."),
|
|
447
|
+
hint=get_http_error_hint(code),
|
|
448
|
+
render_output=_render_build_log_error,
|
|
449
|
+
)
|
|
410
450
|
|
|
411
451
|
except (TooManyRetriesError, TimeoutError):
|
|
412
452
|
message = "Lost connection to build log stream. Please try again later."
|
|
@@ -1,31 +1,10 @@
|
|
|
1
|
-
from pydantic import BaseModel
|
|
2
1
|
from rich.text import Text
|
|
3
2
|
|
|
4
|
-
from fastapi_cloud_cli.api import
|
|
3
|
+
from fastapi_cloud_cli.api import EnvironmentVariable
|
|
5
4
|
|
|
6
5
|
ENV_VAR_VALUE_MAX_LENGTH = 40
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
class EnvironmentVariable(BaseModel):
|
|
10
|
-
name: str
|
|
11
|
-
value: str | None = None
|
|
12
|
-
is_secret: bool = False
|
|
13
|
-
updated_at: str | None = None
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class EnvironmentVariableResponse(BaseModel):
|
|
17
|
-
data: list[EnvironmentVariable]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _get_environment_variables(
|
|
21
|
-
client: APIClient, app_id: str
|
|
22
|
-
) -> EnvironmentVariableResponse:
|
|
23
|
-
response = client.get(f"/apps/{app_id}/environment-variables/")
|
|
24
|
-
response.raise_for_status()
|
|
25
|
-
|
|
26
|
-
return EnvironmentVariableResponse.model_validate(response.json())
|
|
27
|
-
|
|
28
|
-
|
|
29
8
|
def _find_environment_variable(
|
|
30
9
|
environment_variables: list[EnvironmentVariable], name: str
|
|
31
10
|
) -> EnvironmentVariable | None:
|
|
@@ -9,7 +9,6 @@ from rich_toolkit.menu import Option
|
|
|
9
9
|
from fastapi_cloud_cli.api import APIClient
|
|
10
10
|
from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context
|
|
11
11
|
from fastapi_cloud_cli.commands.env._app import env_app
|
|
12
|
-
from fastapi_cloud_cli.commands.env._shared import _get_environment_variables
|
|
13
12
|
from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail
|
|
14
13
|
from fastapi_cloud_cli.utils.env import validate_environment_variable_name
|
|
15
14
|
from fastapi_cloud_cli.utils.execution import JsonOutputOption
|
|
@@ -32,17 +31,6 @@ def _render_environment_variable_delete_output(
|
|
|
32
31
|
toolkit.print(f"Environment variable [bold]{data.name}[/] deleted.", bullet=False)
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
def _delete_environment_variable(client: APIClient, app_id: str, name: str) -> bool:
|
|
36
|
-
response = client.delete(f"/apps/{app_id}/environment-variables/{name}")
|
|
37
|
-
|
|
38
|
-
if response.status_code == 404:
|
|
39
|
-
return False
|
|
40
|
-
|
|
41
|
-
response.raise_for_status()
|
|
42
|
-
|
|
43
|
-
return True
|
|
44
|
-
|
|
45
|
-
|
|
46
34
|
@env_app.command(cls=UserCommand)
|
|
47
35
|
def delete(
|
|
48
36
|
ctx: typer.Context,
|
|
@@ -84,10 +72,19 @@ def delete(
|
|
|
84
72
|
help="Confirm deletion without prompting.",
|
|
85
73
|
),
|
|
86
74
|
] = False,
|
|
75
|
+
no_redeploy: Annotated[
|
|
76
|
+
bool,
|
|
77
|
+
typer.Option(
|
|
78
|
+
"--no-redeploy",
|
|
79
|
+
help="Delete the environment variable without redeploying the app.",
|
|
80
|
+
),
|
|
81
|
+
] = False,
|
|
87
82
|
json_output: JsonOutputOption = False,
|
|
88
83
|
) -> Any:
|
|
89
84
|
"""
|
|
90
|
-
Delete an environment variable
|
|
85
|
+
Delete an environment variable and redeploy the app by default.
|
|
86
|
+
|
|
87
|
+
Succeeds even if the variable is already absent.
|
|
91
88
|
"""
|
|
92
89
|
|
|
93
90
|
toolkit = get_user_command_context(ctx).toolkit
|
|
@@ -109,9 +106,9 @@ def delete(
|
|
|
109
106
|
with toolkit.progress(
|
|
110
107
|
"Fetching environment variables...", transient=True
|
|
111
108
|
) as progress:
|
|
112
|
-
with client.handle_http_errors(progress):
|
|
113
|
-
environment_variables =
|
|
114
|
-
|
|
109
|
+
with client.handle_http_errors(progress, toolkit=toolkit):
|
|
110
|
+
environment_variables = client.get_environment_variables(
|
|
111
|
+
app_id=target_app_id
|
|
115
112
|
)
|
|
116
113
|
|
|
117
114
|
toolkit.print_title("environment variables")
|
|
@@ -163,23 +160,14 @@ def delete(
|
|
|
163
160
|
with toolkit.progress(
|
|
164
161
|
"Deleting environment variable", transient=True
|
|
165
162
|
) as progress:
|
|
166
|
-
with client.handle_http_errors(progress):
|
|
167
|
-
|
|
168
|
-
|
|
163
|
+
with client.handle_http_errors(progress, toolkit=toolkit):
|
|
164
|
+
client.batch_environment_variables(
|
|
165
|
+
app_id=target_app_id,
|
|
166
|
+
upsert={},
|
|
167
|
+
delete=[name],
|
|
168
|
+
redeploy=not no_redeploy,
|
|
169
169
|
)
|
|
170
170
|
|
|
171
|
-
if not deleted:
|
|
172
|
-
message = (
|
|
173
|
-
f"Environment variable {name} not found."
|
|
174
|
-
if toolkit.mode == "json"
|
|
175
|
-
else "Environment variable not found."
|
|
176
|
-
)
|
|
177
|
-
toolkit.fail(
|
|
178
|
-
"not_found",
|
|
179
|
-
message,
|
|
180
|
-
hint="Run `fastapi cloud env list` to see available variables.",
|
|
181
|
-
)
|
|
182
|
-
|
|
183
171
|
toolkit.success(
|
|
184
172
|
EnvironmentVariableDeleteOutput(
|
|
185
173
|
app_id=target_app_id, name=name, show_tag=name_provided
|
|
@@ -7,14 +7,12 @@ from rich.table import Table
|
|
|
7
7
|
from rich_toolkit import RichToolkit
|
|
8
8
|
from rich_toolkit.menu import Option
|
|
9
9
|
|
|
10
|
-
from fastapi_cloud_cli.api import APIClient
|
|
10
|
+
from fastapi_cloud_cli.api import APIClient, EnvironmentVariable
|
|
11
11
|
from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context
|
|
12
12
|
from fastapi_cloud_cli.commands.env._app import env_app
|
|
13
13
|
from fastapi_cloud_cli.commands.env._shared import (
|
|
14
|
-
EnvironmentVariable,
|
|
15
14
|
_find_environment_variable,
|
|
16
15
|
_format_env_var_value,
|
|
17
|
-
_get_environment_variables,
|
|
18
16
|
)
|
|
19
17
|
from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail
|
|
20
18
|
from fastapi_cloud_cli.utils.execution import JsonOutputOption
|
|
@@ -92,8 +90,8 @@ def get_variable(
|
|
|
92
90
|
"Fetching environment variables...", transient=True
|
|
93
91
|
) as progress:
|
|
94
92
|
with client.handle_http_errors(progress):
|
|
95
|
-
environment_variables =
|
|
96
|
-
|
|
93
|
+
environment_variables = client.get_environment_variables(
|
|
94
|
+
app_id=target_app_id
|
|
97
95
|
)
|
|
98
96
|
|
|
99
97
|
if name is None:
|
|
@@ -7,14 +7,12 @@ from rich.table import Table
|
|
|
7
7
|
from rich.text import Text
|
|
8
8
|
from rich_toolkit import RichToolkit
|
|
9
9
|
|
|
10
|
-
from fastapi_cloud_cli.api import APIClient
|
|
10
|
+
from fastapi_cloud_cli.api import APIClient, EnvironmentVariable
|
|
11
11
|
from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context
|
|
12
12
|
from fastapi_cloud_cli.commands.env._app import env_app
|
|
13
13
|
from fastapi_cloud_cli.commands.env._shared import (
|
|
14
14
|
ENV_VAR_VALUE_MAX_LENGTH,
|
|
15
|
-
EnvironmentVariable,
|
|
16
15
|
_format_env_var_value,
|
|
17
|
-
_get_environment_variables,
|
|
18
16
|
)
|
|
19
17
|
from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail
|
|
20
18
|
from fastapi_cloud_cli.utils.dates import format_last_updated
|
|
@@ -94,8 +92,8 @@ def list_variables(
|
|
|
94
92
|
"Fetching environment variables...", transient=True
|
|
95
93
|
) as progress:
|
|
96
94
|
with client.handle_http_errors(progress):
|
|
97
|
-
environment_variables =
|
|
98
|
-
|
|
95
|
+
environment_variables = client.get_environment_variables(
|
|
96
|
+
app_id=target_app_id
|
|
99
97
|
)
|
|
100
98
|
|
|
101
99
|
toolkit.success(
|
|
@@ -6,7 +6,7 @@ import typer
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
7
|
from rich_toolkit import RichToolkit
|
|
8
8
|
|
|
9
|
-
from fastapi_cloud_cli.api import APIClient
|
|
9
|
+
from fastapi_cloud_cli.api import APIClient, EnvironmentVariableCreatePayload
|
|
10
10
|
from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context
|
|
11
11
|
from fastapi_cloud_cli.commands.env._app import env_app
|
|
12
12
|
from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail
|
|
@@ -95,16 +95,6 @@ def _resolve_environment_variable_value(
|
|
|
95
95
|
return _input(toolkit, "Enter the value of the environment variable:")
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def _set_environment_variable(
|
|
99
|
-
client: APIClient, app_id: str, name: str, value: str, is_secret: bool = False
|
|
100
|
-
) -> None:
|
|
101
|
-
response = client.post(
|
|
102
|
-
f"/apps/{app_id}/environment-variables/",
|
|
103
|
-
json={"name": name, "value": value, "is_secret": is_secret},
|
|
104
|
-
)
|
|
105
|
-
response.raise_for_status()
|
|
106
|
-
|
|
107
|
-
|
|
108
98
|
@env_app.command(cls=UserCommand)
|
|
109
99
|
def set(
|
|
110
100
|
ctx: typer.Context,
|
|
@@ -153,13 +143,20 @@ def set(
|
|
|
153
143
|
bool,
|
|
154
144
|
typer.Option(
|
|
155
145
|
"--secret",
|
|
156
|
-
help="Mark
|
|
146
|
+
help="Mark a new environment variable as secret. Existing variables keep their secret status.",
|
|
147
|
+
),
|
|
148
|
+
] = False,
|
|
149
|
+
no_redeploy: Annotated[
|
|
150
|
+
bool,
|
|
151
|
+
typer.Option(
|
|
152
|
+
"--no-redeploy",
|
|
153
|
+
help="Save the environment variable without redeploying the app.",
|
|
157
154
|
),
|
|
158
155
|
] = False,
|
|
159
156
|
json_output: JsonOutputOption = False,
|
|
160
157
|
) -> Any:
|
|
161
158
|
"""
|
|
162
|
-
|
|
159
|
+
Create or update an environment variable and redeploy the app by default.
|
|
163
160
|
"""
|
|
164
161
|
|
|
165
162
|
toolkit = get_user_command_context(ctx).toolkit
|
|
@@ -190,20 +187,27 @@ def set(
|
|
|
190
187
|
with toolkit.progress(
|
|
191
188
|
"Setting environment variable", transient=True
|
|
192
189
|
) as progress:
|
|
193
|
-
with client.handle_http_errors(progress):
|
|
194
|
-
|
|
195
|
-
client=client,
|
|
190
|
+
with client.handle_http_errors(progress, toolkit=toolkit):
|
|
191
|
+
environment_variables = client.batch_environment_variables(
|
|
196
192
|
app_id=target_app_id,
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
upsert={
|
|
194
|
+
name: EnvironmentVariableCreatePayload(
|
|
195
|
+
value=value, is_secret=secret
|
|
196
|
+
)
|
|
197
|
+
},
|
|
198
|
+
delete=[],
|
|
199
|
+
redeploy=not no_redeploy,
|
|
200
200
|
)
|
|
201
201
|
|
|
202
|
+
variable = next(
|
|
203
|
+
variable for variable in environment_variables.data if variable.name == name
|
|
204
|
+
)
|
|
205
|
+
|
|
202
206
|
toolkit.success(
|
|
203
207
|
EnvironmentVariableSetOutput(
|
|
204
208
|
app_id=target_app_id,
|
|
205
209
|
name=name,
|
|
206
|
-
is_secret=
|
|
210
|
+
is_secret=variable.is_secret,
|
|
207
211
|
show_tag=not prompts_user,
|
|
208
212
|
),
|
|
209
213
|
render_output=_render_environment_variable_set_output,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from rich.markup import escape
|
|
2
|
+
from rich.text import Text
|
|
3
|
+
|
|
4
|
+
from fastapi_cloud_cli.api import BuildFailure
|
|
5
|
+
from fastapi_cloud_cli.utils.cli import FastAPIRichToolkit
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def print_build_error(toolkit: FastAPIRichToolkit, error: BuildFailure) -> None:
|
|
9
|
+
toolkit.print(Text(error.error_title, style="bold red"), emoji="🚨")
|
|
10
|
+
toolkit.print_line()
|
|
11
|
+
toolkit.print(Text(error.error_message))
|
|
12
|
+
if error.error_hint:
|
|
13
|
+
toolkit.print_line()
|
|
14
|
+
toolkit.print_hint(escape(error.error_hint))
|
fastapi_cloud_cli/utils/cli.py
CHANGED
|
@@ -282,7 +282,11 @@ class FastAPIStyle(BaseStyle):
|
|
|
282
282
|
prefix = Text(" " * self.content_padding)
|
|
283
283
|
|
|
284
284
|
if emoji:
|
|
285
|
-
|
|
285
|
+
bullet = Text.from_markup(emoji)
|
|
286
|
+
bullet.plain = self.symbol(
|
|
287
|
+
bullet.plain, fallback=" x" if emoji == ERROR_BULLET else "*"
|
|
288
|
+
)
|
|
289
|
+
prefix.append_text(bullet)
|
|
286
290
|
|
|
287
291
|
prefix.pad_right(
|
|
288
292
|
self.content_padding + self.emoji_column_width - prefix.cell_len
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fastapi-cloud-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.26.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
|
|
@@ -33,7 +33,7 @@ Requires-Dist: typer>=0.26.1
|
|
|
33
33
|
Requires-Dist: uvicorn[standard]>=0.17.6
|
|
34
34
|
Requires-Dist: rignore>=0.5.1
|
|
35
35
|
Requires-Dist: httpx>=0.27.0
|
|
36
|
-
Requires-Dist: rich-toolkit>=0.20.
|
|
36
|
+
Requires-Dist: rich-toolkit>=0.20.5
|
|
37
37
|
Requires-Dist: pydantic[email]>=2.7.4; python_version < "3.13"
|
|
38
38
|
Requires-Dist: pydantic[email]>=2.8.0; python_version == "3.13"
|
|
39
39
|
Requires-Dist: pydantic[email]>=2.12.0; python_version >= "3.14"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
fastapi_cloud_cli-0.
|
|
2
|
-
fastapi_cloud_cli-0.
|
|
3
|
-
fastapi_cloud_cli-0.
|
|
4
|
-
fastapi_cloud_cli-0.
|
|
5
|
-
fastapi_cloud_cli/__init__.py,sha256=
|
|
1
|
+
fastapi_cloud_cli-0.26.0.dist-info/METADATA,sha256=cGX_kGGfi9Wqny026UsXThsuq5nm8Vyoqu08Ar2uXJE,3108
|
|
2
|
+
fastapi_cloud_cli-0.26.0.dist-info/WHEEL,sha256=VP-D4TPS230sME9Z3vb3INXvo1yt0924YRm5AOsk_dE,90
|
|
3
|
+
fastapi_cloud_cli-0.26.0.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
|
4
|
+
fastapi_cloud_cli-0.26.0.dist-info/licenses/LICENSE,sha256=FqD5B4VbXJnefprQseE0U8llL6FxojC-i8muZy7YmSU,1086
|
|
5
|
+
fastapi_cloud_cli/__init__.py,sha256=S1dNBNSmHJkAHkq1vL1Yg-dFpQk0fri_CstNlL1u5qw,23
|
|
6
6
|
fastapi_cloud_cli/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30
|
|
7
7
|
fastapi_cloud_cli/_app.py,sha256=7RNSWWNLEoIN3WwvTBJ3MSKIPPfbbwoLHFJrSYdJjJc,735
|
|
8
|
-
fastapi_cloud_cli/api/__init__.py,sha256=
|
|
8
|
+
fastapi_cloud_cli/api/__init__.py,sha256=ORZqOHW7yaKjq0jY7M5pK8J5oyFgMOrWNeBzXMC2fCA,1359
|
|
9
9
|
fastapi_cloud_cli/api/_errors.py,sha256=NJVIfaKsPDZi-7Y5JlZU0qsZc3BBozEC1bb1RKdGs2Q,3711
|
|
10
|
-
fastapi_cloud_cli/api/_models.py,sha256=
|
|
10
|
+
fastapi_cloud_cli/api/_models.py,sha256=9h546GVlEi9CJ7vWt7UNfcpubuvnJ2pUgs33gbKrqVc,5469
|
|
11
11
|
fastapi_cloud_cli/api/_retry.py,sha256=SsSG2jid2RjwpHpKRU_YfzzjLKAnk17OjgkyQgPLzxo,2706
|
|
12
|
-
fastapi_cloud_cli/api/client.py,sha256=
|
|
12
|
+
fastapi_cloud_cli/api/client.py,sha256=kzrU6nx-L2vWJZ99-KCzTpZIPfhVMwrcRcqaMx9TAo4,10884
|
|
13
13
|
fastapi_cloud_cli/cli.py,sha256=u-ZYE-mN0Ml5XNy11iMR_W8WCqp8d2J6BZ7YH05-CkM,3989
|
|
14
14
|
fastapi_cloud_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
fastapi_cloud_cli/commands/_auth.py,sha256=8LKkpQnEoBwJbU_LvIPsDrACuIfc1cyI7xXRpGEPLvw,1199
|
|
@@ -34,8 +34,8 @@ fastapi_cloud_cli/commands/deploy/cloud.py,sha256=ISsO-cQpdsw2Ia6R9P-TIOMKx1VxD1
|
|
|
34
34
|
fastapi_cloud_cli/commands/deploy/command.py,sha256=7RQCqlVrqusLl2ZtWYOfQ95nEoixszbHdfQiuocrgqQ,13967
|
|
35
35
|
fastapi_cloud_cli/commands/deploy/configure.py,sha256=2D9MDknTp5yE5ihIE_oHBNgrPY4fcvaItR2dK1SiXVk,4898
|
|
36
36
|
fastapi_cloud_cli/commands/deploy/upload.py,sha256=TRHKVco06ZKXSLAV80iBy81HswAzSe00GvshOpfkv5s,3185
|
|
37
|
-
fastapi_cloud_cli/commands/deploy/wait.py,sha256=
|
|
38
|
-
fastapi_cloud_cli/commands/deployments.py,sha256=
|
|
37
|
+
fastapi_cloud_cli/commands/deploy/wait.py,sha256=f-4xFftueemMtdONqeA65x0UqksS32bc6XOiZatvFpg,6723
|
|
38
|
+
fastapi_cloud_cli/commands/deployments.py,sha256=ZWbfdkkwkmMqtR8ysrnR9bAlnEAsoPAg9DlQbTJuYfU,14319
|
|
39
39
|
fastapi_cloud_cli/commands/domains/__init__.py,sha256=t0kcyNvJVHITc6xPWvsTMKCUpWd3Y_Ur-F2V66UyGeY,106
|
|
40
40
|
fastapi_cloud_cli/commands/domains/_app.py,sha256=h-6saPMt--q5vUM3fKxI-kf0c7Ej-5bY9lXYMVsMF9I,120
|
|
41
41
|
fastapi_cloud_cli/commands/domains/_setup.py,sha256=YiY8uF9QAq3UKf2vTI6sryAlsqGXlfNgYR1qLU6b7g4,6710
|
|
@@ -48,11 +48,11 @@ fastapi_cloud_cli/commands/domains/rendering.py,sha256=mqDMDTaZLvOmDbPO-vrqj0IXB
|
|
|
48
48
|
fastapi_cloud_cli/commands/domains/restart.py,sha256=Ph3Q4WhtePuAJxdzOYp6YISqUYYGOIVEc-rbsqArbmU,4836
|
|
49
49
|
fastapi_cloud_cli/commands/env/__init__.py,sha256=X3cOoOwBCn8jgoIPWMPGRiRzPfUomiavIS04q5gWa24,90
|
|
50
50
|
fastapi_cloud_cli/commands/env/_app.py,sha256=Ip-tTBKpl_xUhMwnYlmvep7tu-VC3gFHo_9IWklBg_k,123
|
|
51
|
-
fastapi_cloud_cli/commands/env/_shared.py,sha256=
|
|
52
|
-
fastapi_cloud_cli/commands/env/delete.py,sha256=
|
|
53
|
-
fastapi_cloud_cli/commands/env/get.py,sha256=
|
|
54
|
-
fastapi_cloud_cli/commands/env/list.py,sha256=
|
|
55
|
-
fastapi_cloud_cli/commands/env/set.py,sha256=
|
|
51
|
+
fastapi_cloud_cli/commands/env/_shared.py,sha256=dDtLjcv5AK7v9N_BPD4fZaElc1Mu3xBBpHA78kbeyTI,855
|
|
52
|
+
fastapi_cloud_cli/commands/env/delete.py,sha256=baILA8agCL41h2MNbbdPqj5k600AAG7HYboxveGo2Ag,5601
|
|
53
|
+
fastapi_cloud_cli/commands/env/get.py,sha256=4IzcRM0Np7rvvHijbkt3DTtcbEUdhgiVnGUE65NiSSw,3978
|
|
54
|
+
fastapi_cloud_cli/commands/env/list.py,sha256=oeqjX-eofuidbyoZY31x2bpm0aZAavDhmT3KW3n5jww,3271
|
|
55
|
+
fastapi_cloud_cli/commands/env/set.py,sha256=wGOFeaat-W1OE8VnTeayezlmcPEeeIHznEEcYj4wELk,6247
|
|
56
56
|
fastapi_cloud_cli/commands/integrations/__init__.py,sha256=0rxfgiemdH6QLzxeA1egGi9T6JVw8EiBV_cOF4cTe-I,424
|
|
57
57
|
fastapi_cloud_cli/commands/integrations/providers/__init__.py,sha256=XalsF3zAyPvlDsS8a7U9aGtkL2PZz6trlcUFyOdUTAo,136
|
|
58
58
|
fastapi_cloud_cli/commands/integrations/providers/_app.py,sha256=IkgPmQfkRb0ajimz0D7yqaLRciy8tWOufVxdFNer_rU,124
|
|
@@ -84,7 +84,8 @@ fastapi_cloud_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
84
84
|
fastapi_cloud_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
85
|
fastapi_cloud_cli/utils/apps.py,sha256=-qVIxpuXLlRFywDzY9-ZtFcwzFQ0cdFE0bQcQqIN33o,2722
|
|
86
86
|
fastapi_cloud_cli/utils/auth.py,sha256=O4MzQ1jj16hkO8-NPD1TGUSAabbb-GXx2QLY7vVyyjs,3951
|
|
87
|
-
fastapi_cloud_cli/utils/
|
|
87
|
+
fastapi_cloud_cli/utils/build_logs.py,sha256=rVFz1OyedADpuemBwL2mIVSrkjBPEwoD_nAarPkPluY,500
|
|
88
|
+
fastapi_cloud_cli/utils/cli.py,sha256=q11avEJcjBlOpifeTnsDX--BxMGR2aXVbHKgxdPrFhY,14635
|
|
88
89
|
fastapi_cloud_cli/utils/config.py,sha256=qxPHFmzfJWfm_sfc-DKFPoNTc5jFK7_Bnfy6DVhWigE,700
|
|
89
90
|
fastapi_cloud_cli/utils/dates.py,sha256=cnBPp1f2cDj8_3vWt1RnOrbGVYh7Nr31n95gN5t2RSI,1081
|
|
90
91
|
fastapi_cloud_cli/utils/env.py,sha256=a9S6ehNGxhKbQHXxFVJtjqMEkodKwKa0wOvRVh9VBjw,125
|
|
@@ -94,4 +95,4 @@ fastapi_cloud_cli/utils/progress_file.py,sha256=GMK-OgS1pr2IgfBpJgwPqkb2k37W0b2C
|
|
|
94
95
|
fastapi_cloud_cli/utils/sentry.py,sha256=C_KlTV8JCtB3slrCJLNoTo6beM87rWjxAr92uwmWLR0,599
|
|
95
96
|
fastapi_cloud_cli/utils/teams.py,sha256=PSCL-hySKcUU_pz45xBEKeB8NCbWLBCGlgx_GFLZ-2w,2007
|
|
96
97
|
fastapi_cloud_cli/utils/version_check.py,sha256=-iOus8nWNsQn9FnC5tZD0iCftjUdeCj-RFskPLz1KZE,5209
|
|
97
|
-
fastapi_cloud_cli-0.
|
|
98
|
+
fastapi_cloud_cli-0.26.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|