Coverage for src/fastapi_cloud_cli/commands/env/set.py: 100%
64 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
1import sys 1JKLMNOP
2from pathlib import Path 1JKLMNOP
3from typing import Annotated, Any 1JKLMNOP
5import typer 1JKLMNOP
6from pydantic import BaseModel, Field 1JKLMNOP
7from rich_toolkit import RichToolkit 1JKLMNOP
9from fastapi_cloud_cli.api import APIClient, EnvironmentVariableCreatePayload 1JKLMNOP
10from fastapi_cloud_cli.commands._auth import UserCommand, get_user_command_context 1JKLMNOP
11from fastapi_cloud_cli.commands.env._app import env_app 1JKLMNOP
12from fastapi_cloud_cli.utils.apps import resolve_app_id_or_fail 1JKLMNOP
13from fastapi_cloud_cli.utils.cli import FastAPIRichToolkit 1JKLMNOP
14from fastapi_cloud_cli.utils.execution import JsonOutputOption 1JKLMNOP
17class EnvironmentVariableSetOutput(BaseModel): 1JKLMNOP
18 app_id: str 1JKLMNOP
19 name: str 1JKLMNOP
20 is_secret: bool 1JKLMNOP
21 show_tag: Annotated[bool, Field(exclude=True)] = True 1JKLMNOP
24def _render_environment_variable_set_output( 1JKoLMpNOPq
25 data: EnvironmentVariableSetOutput, toolkit: RichToolkit
26) -> None:
27 kind = "Secret environment variable" if data.is_secret else "Environment variable" 1abrstcduvoefwxyghzApijBCDklEFGmnHIq
28 message = f"{kind} [bold]{data.name}[/] set." 1abrstcduvoefwxyghzApijBCDklEFGmnHIq
30 if data.show_tag: 1abrstcduvoefwxyghzApijBCDklEFGmnHIq
31 toolkit.print_title("environment variables") 1rstuvowxyzApBCDEFGHIq
33 toolkit.print_line() 1abrstcduvoefwxyghzApijBCDklEFGmnHIq
34 toolkit.print(message, bullet=False) 1abrstcduvoefwxyghzApijBCDklEFGmnHIq
37def _input( 1JKLMNOP
38 toolkit: FastAPIRichToolkit,
39 prompt: str,
40 *,
41 password: bool = False,
42) -> str:
43 return toolkit.input(prompt, password=password, bullet=False) 1abcdefghijklmn
46def _resolve_environment_variable_name( 1JKLMNOP
47 toolkit: FastAPIRichToolkit, *, name: str | None, secret: bool
48) -> str:
49 if name is not None: 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
50 return name 2+ ` cbX Q Y r , s Z t - { db0 R 1 u . v 2 o / | eb3 S 4 w : x 5 y ; } fb6 T 7 z = A 8 p ? ~ gb9 U ! B @ C # D [ abhb$ V % E ] F ' G ^ bbib( W ) H _ I * q
52 if toolkit.mode == "json": 2a b jbc d kbe f lbg h mbi j nbk l obm n pb
53 toolkit.fail( 2jbkblbmbnbobpb
54 "missing_required_input",
55 "Environment variable name is required.",
56 hint="Pass NAME to choose an environment variable.",
57 )
59 if secret: 1abcdefghijklmn
60 return _input(toolkit, "Enter the name of the secret to set:") 1bdfhjln
62 return _input(toolkit, "Enter the name of the environment variable to set:") 1acegikm
65def _resolve_environment_variable_value( 1JKLMNOP
66 toolkit: FastAPIRichToolkit,
67 *,
68 value: str | None,
69 value_stdin: bool,
70 secret: bool,
71) -> str:
72 if value is not None and value_stdin: 2a b + ` cbX Q Y r , s Z t c d - { db0 R 1 u . v 2 o e f / | eb3 S 4 w : x 5 y g h ; } fb6 T 7 z = A 8 p i j ? ~ gb9 U ! B @ C # D k l [ abhb$ V % E ] F ' G m n ^ bbib( W ) H _ I * q
73 toolkit.fail( 2cbdbebfbgbhbib
74 "invalid_input",
75 "Only one environment variable value source can be used.",
76 hint="Pass either VALUE or --value-stdin.",
77 )
79 if value is not None: 2a b + ` X Q Y r , s Z t c d - { 0 R 1 u . v 2 o e f / | 3 S 4 w : x 5 y g h ; } 6 T 7 z = A 8 p i j ? ~ 9 U ! B @ C # D k l [ ab$ V % E ] F ' G m n ^ bb( W ) H _ I * q
80 return value 1+XYr,sZt-01u.v2o/34w:x5y;67z=A8p?9!B@C#D[$%E]F'G^()H_I*q
82 if value_stdin: 2a b ` Q c d { R e f | S g h } T i j ~ U k l abV m n bbW
83 return sys.stdin.read().rstrip("\r\n") 1QRSTUVW
85 if toolkit.mode == "json": 2a b ` c d { e f | g h } i j ~ k l abm n bb
86 toolkit.fail( 2` { | } ~ abbb
87 "missing_required_input",
88 "Environment variable value is required.",
89 hint="Pass VALUE or --value-stdin to set the environment variable.",
90 )
92 if secret: 1abcdefghijklmn
93 return _input(toolkit, "Enter the secret value:", password=True) 1bdfhjln
95 return _input(toolkit, "Enter the value of the environment variable:") 1acegikm
98@env_app.command(cls=UserCommand) 1JKLMNOP
99def set( 2J K xbL M ybN O P zb
100 ctx: typer.Context,
101 name: str | None = typer.Argument(
102 None,
103 help="The name of the environment variable to set",
104 ),
105 value: str | None = typer.Argument(
106 None,
107 help="The value of the environment variable to set",
108 ),
109 path_arg: Annotated[
110 Path | None,
111 typer.Argument(
112 help=(
113 "Path to the directory with your app's pyproject.toml "
114 "(defaults to current directory)"
115 )
116 ),
117 ] = None,
118 value_stdin: Annotated[
119 bool,
120 typer.Option(
121 "--value-stdin",
122 help="Read the environment variable value from stdin.",
123 ),
124 ] = False,
125 path: Annotated[
126 Path | None,
127 typer.Option(
128 "--path",
129 help=(
130 "Path to the directory with your app's pyproject.toml "
131 "(defaults to current directory)"
132 ),
133 ),
134 ] = None,
135 app_id: Annotated[
136 str | None,
137 typer.Option(
138 "--app-id",
139 help="ID of the app whose environment variable should be set.",
140 ),
141 ] = None,
142 secret: Annotated[
143 bool,
144 typer.Option(
145 "--secret",
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.",
154 ),
155 ] = False,
156 json_output: JsonOutputOption = False,
157) -> Any:
158 """
159 Create or update an environment variable and redeploy the app by default.
160 """
162 toolkit = get_user_command_context(ctx).toolkit 2a b + jb` cbX Q Y r qb, s Z t c d - kb{ db0 R 1 u rb. v 2 o e f / lb| eb3 S 4 w sb: x 5 y g h ; mb} fb6 T 7 z tb= A 8 p i j ? nb~ gb9 U ! B ub@ C # D k l [ obabhb$ V % E vb] F ' G m n ^ pbbbib( W ) H wb_ I * q
164 target_app_id = resolve_app_id_or_fail( 2a b + jb` cbX Q Y r qb, s Z t c d - kb{ db0 R 1 u rb. v 2 o e f / lb| eb3 S 4 w sb: x 5 y g h ; mb} fb6 T 7 z tb= A 8 p i j ? nb~ gb9 U ! B ub@ C # D k l [ obabhb$ V % E vb] F ' G m n ^ pbbbib( W ) H wb_ I * q
165 toolkit, app_id=app_id, path=path or path_arg
166 )
167 name_needs_prompt = name is None 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
168 value_needs_prompt = value is None and not value_stdin 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
169 prompts_user = name_needs_prompt or value_needs_prompt 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
170 if prompts_user and toolkit.mode != "json": 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
171 toolkit.print_title("environment variables") 1abcdefghijklmn
172 toolkit.print_line() 1abcdefghijklmn
174 name = _resolve_environment_variable_name( 2a b + jb` cbX Q Y r , s Z t c d - kb{ db0 R 1 u . v 2 o e f / lb| eb3 S 4 w : x 5 y g h ; mb} fb6 T 7 z = A 8 p i j ? nb~ gb9 U ! B @ C # D k l [ obabhb$ V % E ] F ' G m n ^ pbbbib( W ) H _ I * q
175 toolkit,
176 name=name,
177 secret=secret,
178 )
179 value = _resolve_environment_variable_value( 2a b + ` cbX Q Y r , s Z t c d - { db0 R 1 u . v 2 o e f / | eb3 S 4 w : x 5 y g h ; } fb6 T 7 z = A 8 p i j ? ~ gb9 U ! B @ C # D k l [ abhb$ V % E ] F ' G m n ^ bbib( W ) H _ I * q
180 toolkit,
181 value=value,
182 value_stdin=value_stdin,
183 secret=secret,
184 )
186 with APIClient() as client: 1ab+XQYr,sZtcd-0R1u.v2oef/3S4w:x5ygh;6T7z=A8pij?9U!B@C#Dkl[$V%E]F'Gmn^(W)H_I*q
187 with toolkit.progress( 1ab+XQYr,sZtcd-0R1u.v2oef/3S4w:x5ygh;6T7z=A8pij?9U!B@C#Dkl[$V%E]F'Gmn^(W)H_I*q
188 "Setting environment variable", transient=True
189 ) as progress:
190 with client.handle_http_errors(progress, toolkit=toolkit): 1ab+XQYr,sZtcd-0R1u.v2oef/3S4w:x5ygh;6T7z=A8pij?9U!B@C#Dkl[$V%E]F'Gmn^(W)H_I*q
191 environment_variables = client.batch_environment_variables( 1ab+XQYr,sZtcd-0R1u.v2oef/3S4w:x5ygh;6T7z=A8pij?9U!B@C#Dkl[$V%E]F'Gmn^(W)H_I*q
192 app_id=target_app_id,
193 upsert={
194 name: EnvironmentVariableCreatePayload(
195 value=value, is_secret=secret
196 )
197 },
198 delete=[],
199 redeploy=not no_redeploy,
200 )
202 variable = next( 1abXQYrsZtcd0R1uv2oef3S4wx5ygh6T7zA8pij9U!BC#Dkl$V%EF'Gmn(W)HI*q
203 variable for variable in environment_variables.data if variable.name == name
204 )
206 toolkit.success( 1abXQYrsZtcd0R1uv2oef3S4wx5ygh6T7zA8pij9U!BC#Dkl$V%EF'Gmn(W)HI*q
207 EnvironmentVariableSetOutput(
208 app_id=target_app_id,
209 name=name,
210 is_secret=variable.is_secret,
211 show_tag=not prompts_user,
212 ),
213 render_output=_render_environment_variable_set_output,
214 )