Coverage for src/fastapi_cloud_cli/commands/deploy/configure.py: 100%
57 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-11-09 00:25 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-11-09 00:25 +0000
1from pathlib import Path 2EbFbGbHbIbJbKb
3import typer 2EbFbGbHbIbJbKb
4from pydantic import TypeAdapter 2EbFbGbHbIbJbKb
5from rich_toolkit.menu import Option 2EbFbGbHbIbJbKb
7from fastapi_cloud_cli.api import APIClient 2EbFbGbHbIbJbKb
8from fastapi_cloud_cli.commands.deploy.archive import AppDirectory, _get_app_name 2EbFbGbHbIbJbKb
9from fastapi_cloud_cli.commands.deploy.cloud import ( 2EbFbGbHbIbJbKb
10 AppResponse,
11 _create_app,
12 _get_apps,
13 _get_teams,
14 _update_app,
15)
16from fastapi_cloud_cli.utils.apps import AppConfig, write_app_config 2EbFbGbHbIbJbKb
17from fastapi_cloud_cli.utils.cli import FastAPIRichToolkit 2EbFbGbHbIbJbKb
20def _configure_app( 2EbFbGbHbIbJbKb
21 toolkit: FastAPIRichToolkit,
22 client: APIClient,
23 path_to_deploy: Path,
24) -> AppConfig:
25 toolkit.print(f"Setting up and deploying [blue]{path_to_deploy}[/blue]", emoji="📁") 2qbo = p q r s h t ` LbZb+ 0bcbMbrb1ba db4 5 sbu ? v w x y i z { Nb2b, 3bebObtb4bc fb6 7 ubA @ B C D E j F | Pb5b- 6bgbQbvb7bd hb8 9 wbG [ H I J K k L } Rb8b. 9bibSbxb!be jb! # ybM ] N O P Q l R ~ Tb#b/ $bkbUbzb%bb lb$ % AbS ^ T U V W m X abVb'b: (bmbWbBb)bf nb' ( CbY _ Z 0 1 2 n 3 bbXb*b; +bobYbDb,bg pb) *
27 toolkit.print_line() 2qbo = p q r s h t ` LbZb+ 0bcbMbrb1ba db4 5 sbu ? v w x y i z { Nb2b, 3bebObtb4bc fb6 7 ubA @ B C D E j F | Pb5b- 6bgbQbvb7bd hb8 9 wbG [ H I J K k L } Rb8b. 9bibSbxb!be jb! # ybM ] N O P Q l R ~ Tb#b/ $bkbUbzb%bb lb$ % AbS ^ T U V W m X abVb'b: (bmbWbBb)bf nb' ( CbY _ Z 0 1 2 n 3 bbXb*b; +bobYbDb,bg pb) *
29 with toolkit.progress("Fetching teams...", transient=True) as progress: 2qbo = p q r s h t ` LbZb+ 0bcbMbrb1ba db4 5 sbu ? v w x y i z { Nb2b, 3bebObtb4bc fb6 7 ubA @ B C D E j F | Pb5b- 6bgbQbvb7bd hb8 9 wbG [ H I J K k L } Rb8b. 9bibSbxb!be jb! # ybM ] N O P Q l R ~ Tb#b/ $bkbUbzb%bb lb$ % AbS ^ T U V W m X abVb'b: (bmbWbBb)bf nb' ( CbY _ Z 0 1 2 n 3 bbXb*b; +bobYbDb,bg pb) *
30 with client.handle_http_errors( 2qbo = p q r s h t ` LbZb+ 0bcbMbrb1ba db4 5 sbu ? v w x y i z { Nb2b, 3bebObtb4bc fb6 7 ubA @ B C D E j F | Pb5b- 6bgbQbvb7bd hb8 9 wbG [ H I J K k L } Rb8b. 9bibSbxb!be jb! # ybM ] N O P Q l R ~ Tb#b/ $bkbUbzb%bb lb$ % AbS ^ T U V W m X abVb'b: (bmbWbBb)bf nb' ( CbY _ Z 0 1 2 n 3 bbXb*b; +bobYbDb,bg pb) *
31 progress,
32 default_message="Error fetching teams. Please try again later.",
33 ):
34 teams = _get_teams(client) 2qbo = p q r s h t ` LbZb+ 0bcbMbrb1ba db4 5 sbu ? v w x y i z { Nb2b, 3bebObtb4bc fb6 7 ubA @ B C D E j F | Pb5b- 6bgbQbvb7bd hb8 9 wbG [ H I J K k L } Rb8b. 9bibSbxb!be jb! # ybM ] N O P Q l R ~ Tb#b/ $bkbUbzb%bb lb$ % AbS ^ T U V W m X abVb'b: (bmbWbBb)bf nb' ( CbY _ Z 0 1 2 n 3 bbXb*b; +bobYbDb,bg pb) *
36 team = toolkit.ask( 2qbo = p q r s h t ` Lb+ cbMbrba db4 5 sbu ? v w x y i z { Nb, ebObtbc fb6 7 ubA @ B C D E j F | Pb- gbQbvbd hb8 9 wbG [ H I J K k L } Rb. ibSbxbe jb! # ybM ] N O P Q l R ~ Tb/ kbUbzbb lb$ % AbS ^ T U V W m X abVb: mbWbBbf nb' ( CbY _ Z 0 1 2 n 3 bbXb; obYbDbg pb) *
37 "Select the team you want to deploy to:",
38 options=[
39 Option({"name": team.name, "value": team})
40 for team in sorted(teams, key=lambda team: team.name.lower())
41 ],
42 allow_filtering=True,
43 emoji="🏢",
44 )
46 toolkit.print_line() 2qbo = p q r s h t ` Lb+ cbMbrba db4 5 sbu ? v w x y i z { Nb, ebObtbc fb6 7 ubA @ B C D E j F | Pb- gbQbvbd hb8 9 wbG [ H I J K k L } Rb. ibSbxbe jb! # ybM ] N O P Q l R ~ Tb/ kbUbzbb lb$ % AbS ^ T U V W m X abVb: mbWbBbf nb' ( CbY _ Z 0 1 2 n 3 bbXb; obYbDbg pb) *
48 create_new_app = toolkit.confirm( 2qbo = p q r s h t ` Lb+ cbMbrba db4 5 sbu ? v w x y i z { Nb, ebObtbc fb6 7 ubA @ B C D E j F | Pb- gbQbvbd hb8 9 wbG [ H I J K k L } Rb. ibSbxbe jb! # ybM ] N O P Q l R ~ Tb/ kbUbzbb lb$ % AbS ^ T U V W m X abVb: mbWbBbf nb' ( CbY _ Z 0 1 2 n 3 bbXb; obYbDbg pb) *
49 "Do you want to create a new app?", default=True, emoji="📦"
50 )
52 toolkit.print_line() 2qbo = p q r s h t ` + cbrba db4 5 sbu ? v w x y i z { , ebtbc fb6 7 ubA @ B C D E j F | - gbvbd hb8 9 wbG [ H I J K k L } . ibxbe jb! # ybM ] N O P Q l R ~ / kbzbb lb$ % AbS ^ T U V W m X ab: mbBbf nb' ( CbY _ Z 0 1 2 n 3 bb; obDbg pb) *
54 selected_app: AppResponse | None = None 2qbo = p q r s h t ` + cbrba db4 5 sbu ? v w x y i z { , ebtbc fb6 7 ubA @ B C D E j F | - gbvbd hb8 9 wbG [ H I J K k L } . ibxbe jb! # ybM ] N O P Q l R ~ / kbzbb lb$ % AbS ^ T U V W m X ab: mbBbf nb' ( CbY _ Z 0 1 2 n 3 bb; obDbg pb) *
56 if not create_new_app: 2qbo = p q r s h t ` + cbrba db4 5 sbu ? v w x y i z { , ebtbc fb6 7 ubA @ B C D E j F | - gbvbd hb8 9 wbG [ H I J K k L } . ibxbe jb! # ybM ] N O P Q l R ~ / kbzbb lb$ % AbS ^ T U V W m X ab: mbBbf nb' ( CbY _ Z 0 1 2 n 3 bb; obDbg pb) *
57 with toolkit.progress("Fetching apps...", transient=True) as progress: 2h ` cba db4 5 i { ebc fb6 7 j | gbd hb8 9 k } ibe jb! # l ~ kbb lb$ % m abmbf nb' ( n bbobg pb) *
58 with client.handle_http_errors( 2h ` cba db4 5 i { ebc fb6 7 j | gbd hb8 9 k } ibe jb! # l ~ kbb lb$ % m abmbf nb' ( n bbobg pb) *
59 progress,
60 default_message="Error fetching apps. Please try again later.",
61 ):
62 apps = _get_apps(client=client, team_id=team.id) 2h ` cba db4 5 i { ebc fb6 7 j | gbd hb8 9 k } ibe jb! # l ~ kbb lb$ % m abmbf nb' ( n bbobg pb) *
64 if not apps: 2h ` cba db4 5 i { ebc fb6 7 j | gbd hb8 9 k } ibe jb! # l ~ kbb lb$ % m abmbf nb' ( n bbobg pb) *
65 toolkit.fail( 2cbebgbibkbmbob
66 "not_found",
67 "No apps found in this team. You can create a new app instead.",
68 )
70 selected_app = toolkit.ask( 2h ` a db4 5 i { c fb6 7 j | d hb8 9 k } e jb! # l ~ b lb$ % m abf nb' ( n bbg pb) *
71 "Select the app you want to deploy to:",
72 options=[
73 Option({"name": app.slug, "value": app})
74 for app in sorted(apps, key=lambda app: app.slug.lower())
75 ],
76 allow_filtering=True,
77 emoji="📦",
78 )
80 app_name = ( 2qbo = p q r s h t ` + rba 4 5 sbu ? v w x y i z { , tbc 6 7 ubA @ B C D E j F | - vbd 8 9 wbG [ H I J K k L } . xbe ! # ybM ] N O P Q l R ~ / zbb $ % AbS ^ T U V W m X ab: Bbf ' ( CbY _ Z 0 1 2 n 3 bb; Dbg ) *
81 selected_app.slug
82 if selected_app
83 else toolkit.input(
84 title="What's your app name?",
85 default=_get_app_name(path_to_deploy),
86 emoji="✏️",
87 )
88 )
90 toolkit.print_line() 2qbo = p q r s h t ` + rba 4 5 sbu ? v w x y i z { , tbc 6 7 ubA @ B C D E j F | - vbd 8 9 wbG [ H I J K k L } . xbe ! # ybM ] N O P Q l R ~ / zbb $ % AbS ^ T U V W m X ab: Bbf ' ( CbY _ Z 0 1 2 n 3 bb; Dbg ) *
92 initial_directory = selected_app.directory if selected_app else "" 2qbo = p q r s h t ` + rba 4 5 sbu ? v w x y i z { , tbc 6 7 ubA @ B C D E j F | - vbd 8 9 wbG [ H I J K k L } . xbe ! # ybM ] N O P Q l R ~ / zbb $ % AbS ^ T U V W m X ab: Bbf ' ( CbY _ Z 0 1 2 n 3 bb; Dbg ) *
94 directory_input = toolkit.input( 2qbo = p q r s h t ` + rba 4 5 sbu ? v w x y i z { , tbc 6 7 ubA @ B C D E j F | - vbd 8 9 wbG [ H I J K k L } . xbe ! # ybM ] N O P Q l R ~ / zbb $ % AbS ^ T U V W m X ab: Bbf ' ( CbY _ Z 0 1 2 n 3 bb; Dbg ) *
95 title=(
96 "Directory where your app's pyproject.toml file lives (e.g. src, backend):"
97 ),
98 value=initial_directory or "",
99 placeholder=(
100 "[italic]Leave empty if pyproject.toml is in the current directory[/italic]"
101 ),
102 validator=TypeAdapter(AppDirectory),
103 emoji="📂",
104 )
106 directory: str | None = directory_input if directory_input else None 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
108 toolkit.print_line() 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
110 toolkit.print("Deployment configuration:", emoji="📋") 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
111 toolkit.print_line() 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
112 toolkit.print(f"Team: [bold]{team.name}[/bold]") 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
113 toolkit.print(f"App name: [bold]{app_name}[/bold]") 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
114 toolkit.print(f"Directory: [bold]{directory or '.'}[/bold]") 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
116 toolkit.print_line() 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
118 choice = toolkit.ask( 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
119 "Does everything look right?",
120 options=[
121 Option({"name": "Yes, start the deployment!", "value": "deploy"}),
122 Option({"name": "No, let me start over", "value": "cancel"}),
123 ],
124 emoji="👀",
125 )
126 toolkit.print_line() 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
128 if choice == "cancel": 1o=pqrsht+a45u?vwxyiz,c67A@BCDEjF-d89G[HIJKkL.e!#M]NOPQlR/b$%S^TUVWmX:f'(Y_Z012n3;g)*
129 toolkit.print("Deployment cancelled.") 1=45?67@89[!#]$%^'(_)*
130 raise typer.Exit(0) 1=45?67@89[!#]$%^'(_)*
132 if selected_app: 1opqrsht+auvwxyiz,cABCDEjF-dGHIJKkL.eMNOPQlR/bSTUVWmX:fYZ012n3;g
133 if directory != selected_app.directory: 1haicjdkelbmfng
134 with ( 1ab
135 toolkit.progress(title="Updating app directory...") as progress,
136 client.handle_http_errors(progress),
137 ):
138 app = _update_app( 1acdebfg
139 client=client, app_id=selected_app.id, directory=directory
140 )
142 progress.log(f"App directory updated to '{directory or '.'}'") 1acdebfg
143 else:
144 app = selected_app 1hijklmn
145 else:
146 with toolkit.progress(title="Creating app...") as progress: 1opqrst+uvwxyz,ABCDEF-GHIJKL.MNOPQR/STUVWX:YZ0123;
147 with client.handle_http_errors(progress): 1opqrst+uvwxyz,ABCDEF-GHIJKL.MNOPQR/STUVWX:YZ0123;
148 app = _create_app( 1opqrst+uvwxyz,ABCDEF-GHIJKL.MNOPQR/STUVWX:YZ0123;
149 client=client,
150 team_id=team.id,
151 app_name=app_name,
152 directory=directory,
153 )
155 progress.log(f"App created successfully! App slug: {app.slug}") 1opqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123
157 app_config = AppConfig(app_id=app.id, team_id=team.id) 1opqrshtauvwxyizcABCDEjFdGHIJKkLeMNOPQlRbSTUVWmXfYZ012n3g
159 write_app_config(path_to_deploy, app_config) 1opqrshtauvwxyizcABCDEjFdGHIJKkLeMNOPQlRbSTUVWmXfYZ012n3g
161 return app_config 1opqrshtauvwxyizcABCDEjFdGHIJKkLeMNOPQlRbSTUVWmXfYZ012n3g