Skip to content

Commit f467ac4

Browse files
YuriiMotovpatrick91github-actions[bot]
authored
🐛 Fix FASTAPI_CLOUD_TOKEN always overrides user token (#180)
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent c0a5690 commit f467ac4

File tree

17 files changed

+589
-411
lines changed

17 files changed

+589
-411
lines changed

src/fastapi_cloud_cli/commands/deploy.py

Lines changed: 235 additions & 192 deletions
Large diffs are not rendered by default.

src/fastapi_cloud_cli/commands/env.py

Lines changed: 74 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from fastapi_cloud_cli.utils.api import APIClient
99
from fastapi_cloud_cli.utils.apps import get_app_config
1010
from fastapi_cloud_cli.utils.auth import Identity
11-
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
11+
from fastapi_cloud_cli.utils.cli import get_rich_toolkit
1212
from fastapi_cloud_cli.utils.env import validate_environment_variable_name
1313

1414
logger = logging.getLogger(__name__)
@@ -23,17 +23,17 @@ class EnvironmentVariableResponse(BaseModel):
2323
data: list[EnvironmentVariable]
2424

2525

26-
def _get_environment_variables(app_id: str) -> EnvironmentVariableResponse:
27-
with APIClient() as client:
28-
response = client.get(f"/apps/{app_id}/environment-variables/")
29-
response.raise_for_status()
26+
def _get_environment_variables(
27+
client: APIClient, app_id: str
28+
) -> EnvironmentVariableResponse:
29+
response = client.get(f"/apps/{app_id}/environment-variables/")
30+
response.raise_for_status()
3031

31-
return EnvironmentVariableResponse.model_validate(response.json())
32+
return EnvironmentVariableResponse.model_validate(response.json())
3233

3334

34-
def _delete_environment_variable(app_id: str, name: str) -> bool:
35-
with APIClient() as client:
36-
response = client.delete(f"/apps/{app_id}/environment-variables/{name}")
35+
def _delete_environment_variable(client: APIClient, app_id: str, name: str) -> bool:
36+
response = client.delete(f"/apps/{app_id}/environment-variables/{name}")
3737

3838
if response.status_code == 404:
3939
return False
@@ -44,14 +44,13 @@ def _delete_environment_variable(app_id: str, name: str) -> bool:
4444

4545

4646
def _set_environment_variable(
47-
app_id: str, name: str, value: str, is_secret: bool = False
47+
client: APIClient, app_id: str, name: str, value: str, is_secret: bool = False
4848
) -> None:
49-
with APIClient() as client:
50-
response = client.post(
51-
f"/apps/{app_id}/environment-variables/",
52-
json={"name": name, "value": value, "is_secret": is_secret},
53-
)
54-
response.raise_for_status()
49+
response = client.post(
50+
f"/apps/{app_id}/environment-variables/",
51+
json={"name": name, "value": value, "is_secret": is_secret},
52+
)
53+
response.raise_for_status()
5554

5655

5756
env_app = typer.Typer()
@@ -91,11 +90,14 @@ def list(
9190
)
9291
raise typer.Exit(1)
9392

94-
with toolkit.progress(
95-
"Fetching environment variables...", transient=True
96-
) as progress:
97-
with handle_http_errors(progress):
98-
environment_variables = _get_environment_variables(app_config.app_id)
93+
with APIClient() as client:
94+
with toolkit.progress(
95+
"Fetching environment variables...", transient=True
96+
) as progress:
97+
with client.handle_http_errors(progress):
98+
environment_variables = _get_environment_variables(
99+
client=client, app_id=app_config.app_id
100+
)
99101

100102
if not environment_variables.data:
101103
toolkit.print("No environment variables found.")
@@ -146,42 +148,45 @@ def delete(
146148
)
147149
raise typer.Exit(1)
148150

149-
if not name:
150-
with toolkit.progress(
151-
"Fetching environment variables...", transient=True
152-
) as progress:
153-
with handle_http_errors(progress):
154-
environment_variables = _get_environment_variables(
155-
app_config.app_id
156-
)
157-
158-
if not environment_variables.data:
159-
toolkit.print("No environment variables found.")
160-
return
161-
162-
name = toolkit.ask(
163-
"Select the environment variable to delete:",
164-
options=[
165-
{"name": env_var.name, "value": env_var.name}
166-
for env_var in environment_variables.data
167-
],
168-
)
169-
170-
assert name
171-
else:
172-
if not validate_environment_variable_name(name):
173-
toolkit.print(
174-
f"The environment variable name [bold]{name}[/] is invalid."
151+
with APIClient() as client:
152+
if not name:
153+
with toolkit.progress(
154+
"Fetching environment variables...", transient=True
155+
) as progress:
156+
with client.handle_http_errors(progress):
157+
environment_variables = _get_environment_variables(
158+
client=client, app_id=app_config.app_id
159+
)
160+
161+
if not environment_variables.data:
162+
toolkit.print("No environment variables found.")
163+
return
164+
165+
name = toolkit.ask(
166+
"Select the environment variable to delete:",
167+
options=[
168+
{"name": env_var.name, "value": env_var.name}
169+
for env_var in environment_variables.data
170+
],
175171
)
176-
raise typer.Exit(1)
177172

178-
toolkit.print_line()
173+
assert name
174+
else:
175+
if not validate_environment_variable_name(name):
176+
toolkit.print(
177+
f"The environment variable name [bold]{name}[/] is invalid."
178+
)
179+
raise typer.Exit(1)
180+
181+
toolkit.print_line()
179182

180-
with toolkit.progress(
181-
"Deleting environment variable", transient=True
182-
) as progress:
183-
with handle_http_errors(progress):
184-
deleted = _delete_environment_variable(app_config.app_id, name)
183+
with toolkit.progress(
184+
"Deleting environment variable", transient=True
185+
) as progress:
186+
with client.handle_http_errors(progress):
187+
deleted = _delete_environment_variable(
188+
client=client, app_id=app_config.app_id, name=name
189+
)
185190

186191
if not deleted:
187192
toolkit.print("Environment variable not found.")
@@ -253,14 +258,21 @@ def set(
253258
else:
254259
value = toolkit.input("Enter the value of the environment variable:")
255260

256-
with toolkit.progress(
257-
"Setting environment variable", transient=True
258-
) as progress:
259-
assert name is not None
260-
assert value is not None
261-
262-
with handle_http_errors(progress):
263-
_set_environment_variable(app_config.app_id, name, value, secret)
261+
with APIClient() as client:
262+
with toolkit.progress(
263+
"Setting environment variable", transient=True
264+
) as progress:
265+
assert name is not None
266+
assert value is not None
267+
268+
with client.handle_http_errors(progress):
269+
_set_environment_variable(
270+
client=client,
271+
app_id=app_config.app_id,
272+
name=name,
273+
value=value,
274+
is_secret=secret,
275+
)
264276

265277
if secret:
266278
toolkit.print(f"Secret environment variable [bold]{name}[/] set.")

src/fastapi_cloud_cli/commands/link.py

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from fastapi_cloud_cli.utils.api import APIClient
99
from fastapi_cloud_cli.utils.apps import AppConfig, get_app_config, write_app_config
1010
from fastapi_cloud_cli.utils.auth import Identity
11-
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
11+
from fastapi_cloud_cli.utils.cli import get_rich_toolkit
1212

1313
logger = logging.getLogger(__name__)
1414

@@ -47,40 +47,42 @@ def link() -> Any:
4747
toolkit.print_title("Link to FastAPI Cloud", tag="FastAPI")
4848
toolkit.print_line()
4949

50-
with toolkit.progress("Fetching teams...") as progress:
51-
with handle_http_errors(
52-
progress,
53-
default_message="Error fetching teams. Please try again later.",
54-
):
55-
with APIClient() as client:
50+
with APIClient() as client:
51+
with toolkit.progress("Fetching teams...") as progress:
52+
with client.handle_http_errors(
53+
progress,
54+
default_message="Error fetching teams. Please try again later.",
55+
):
5656
response = client.get("/teams/")
5757
response.raise_for_status()
5858
teams_data = response.json()["data"]
5959

60-
if not teams_data:
61-
toolkit.print(
62-
"[error]No teams found. Please create a team first.[/]",
63-
)
64-
raise typer.Exit(1)
60+
if not teams_data:
61+
toolkit.print(
62+
"[error]No teams found. Please create a team first.[/]",
63+
)
64+
raise typer.Exit(1)
6565

66-
toolkit.print_line()
66+
toolkit.print_line()
6767

68-
team = toolkit.ask(
69-
"Select the team:",
70-
tag="team",
71-
options=[
72-
Option({"name": t["name"], "value": {"id": t["id"], "name": t["name"]}})
73-
for t in teams_data
74-
],
75-
)
68+
team = toolkit.ask(
69+
"Select the team:",
70+
tag="team",
71+
options=[
72+
Option(
73+
{"name": t["name"], "value": {"id": t["id"], "name": t["name"]}}
74+
)
75+
for t in teams_data
76+
],
77+
)
7678

77-
toolkit.print_line()
79+
toolkit.print_line()
7880

79-
with toolkit.progress("Fetching apps...") as progress:
80-
with handle_http_errors(
81-
progress, default_message="Error fetching apps. Please try again later."
82-
):
83-
with APIClient() as client:
81+
with toolkit.progress("Fetching apps...") as progress:
82+
with client.handle_http_errors(
83+
progress,
84+
default_message="Error fetching apps. Please try again later.",
85+
):
8486
response = client.get("/apps/", params={"team_id": team["id"]})
8587
response.raise_for_status()
8688
apps_data = response.json()["data"]

src/fastapi_cloud_cli/commands/login.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastapi_cloud_cli.config import Settings
1010
from fastapi_cloud_cli.utils.api import APIClient
1111
from fastapi_cloud_cli.utils.auth import AuthConfig, Identity, write_auth_config
12-
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
12+
from fastapi_cloud_cli.utils.cli import get_rich_toolkit
1313

1414
logger = logging.getLogger(__name__)
1515

@@ -87,13 +87,22 @@ def login() -> Any:
8787

8888
return
8989

90+
if identity.has_deploy_token():
91+
with get_rich_toolkit() as toolkit:
92+
toolkit.print(
93+
"You have [bold blue]FASTAPI_CLOUD_TOKEN[/] environment variable set.\n"
94+
"This token will take precedence over the user token for "
95+
"[blue]`fastapi deploy`[/] command.",
96+
tag="Warning",
97+
)
98+
9099
with get_rich_toolkit() as toolkit, APIClient() as client:
91100
toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI")
92101

93102
toolkit.print_line()
94103

95104
with toolkit.progress("Starting authorization") as progress:
96-
with handle_http_errors(progress):
105+
with client.handle_http_errors(progress):
97106
authorization_data = _start_device_authorization(client)
98107

99108
url = authorization_data.verification_uri_complete
@@ -105,7 +114,7 @@ def login() -> Any:
105114
with toolkit.progress("Waiting for user to authorize...") as progress:
106115
typer.launch(url)
107116

108-
with handle_http_errors(progress):
117+
with client.handle_http_errors(progress):
109118
access_token = _fetch_access_token(
110119
client, authorization_data.device_code, authorization_data.interval
111120
)

src/fastapi_cloud_cli/commands/logs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
AppLogEntry,
1515
StreamLogError,
1616
TooManyRetriesError,
17+
handle_http_error,
1718
)
1819
from fastapi_cloud_cli.utils.apps import AppConfig, get_app_config
1920
from fastapi_cloud_cli.utils.auth import Identity
20-
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_error
21+
from fastapi_cloud_cli.utils.cli import get_rich_toolkit
2122

2223
logger = logging.getLogger(__name__)
2324

src/fastapi_cloud_cli/commands/setup_ci.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from fastapi_cloud_cli.utils.api import APIClient
1111
from fastapi_cloud_cli.utils.apps import get_app_config
1212
from fastapi_cloud_cli.utils.auth import Identity
13-
from fastapi_cloud_cli.utils.cli import get_rich_toolkit, handle_http_errors
13+
from fastapi_cloud_cli.utils.cli import get_rich_toolkit
1414

1515
logger = logging.getLogger(__name__)
1616

@@ -96,19 +96,18 @@ def _set_github_secret(name: str, value: str) -> None:
9696
raise GitHubSecretError(f"Failed to set GitHub secret '{name}'") from e
9797

9898

99-
def _create_token(app_id: str, token_name: str) -> dict[str, str]:
99+
def _create_token(client: APIClient, app_id: str, token_name: str) -> dict[str, str]:
100100
"""Create a new deploy token.
101101
102102
Returns token_data dict with 'value' and 'expired_at' keys.
103103
"""
104-
with APIClient() as client:
105-
response = client.post(
106-
f"/apps/{app_id}/tokens",
107-
json={"name": token_name, "expires_in_days": TOKEN_EXPIRES_DAYS},
108-
)
109-
response.raise_for_status()
110-
data = response.json()
111-
return {"value": data["value"], "expired_at": data["expired_at"]}
104+
response = client.post(
105+
f"/apps/{app_id}/tokens",
106+
json={"name": token_name, "expires_in_days": TOKEN_EXPIRES_DAYS},
107+
)
108+
response.raise_for_status()
109+
data = response.json()
110+
return {"value": data["value"], "expired_at": data["expired_at"]}
112111

113112

114113
def _get_default_branch() -> str:
@@ -285,12 +284,15 @@ def setup_ci(
285284
token_name = f"GitHub Actions — {repo_slug} ({timestamp})"
286285

287286
with (
287+
APIClient() as client,
288288
toolkit.progress(title="Generating deploy token...") as progress,
289-
handle_http_errors(
289+
client.handle_http_errors(
290290
progress, default_message="Error creating deploy token."
291291
),
292292
):
293-
token_data = _create_token(app_config.app_id, token_name)
293+
token_data = _create_token(
294+
client=client, app_id=app_config.app_id, token_name=token_name
295+
)
294296
progress.log(msg_token)
295297

296298
toolkit.print_line()

0 commit comments

Comments
 (0)