iron/cli.rs
1use clap::{Parser, Subcommand};
2
3#[derive(Parser)]
4#[command(name = "flow", about = "Deploy and manage the Flow fleet")]
5pub struct Cli {
6 #[command(subcommand)]
7 pub command: Command,
8
9 /// Path to fleet.toml (default: fleet.toml in current directory)
10 #[arg(long, global = true, default_value = "fleet.toml")]
11 pub config: String,
12}
13
14#[derive(Subcommand)]
15pub enum Command {
16 /// Deploy an app (or all apps if no name given)
17 Deploy {
18 /// App name to deploy (deploys all if omitted)
19 app: Option<String>,
20
21 /// Force recreate containers instead of rolling deploy
22 #[arg(long)]
23 force: bool,
24 },
25
26 /// Verify fleet.toml matches reality on servers
27 Check {
28 /// Filter by server name
29 #[arg(long)]
30 server: Option<String>,
31
32 /// Re-run Ansible hardening playbook
33 #[arg(long)]
34 with_hardening: bool,
35 },
36
37 /// Show fleet-wide status and container info
38 Status {
39 /// Filter by server name
40 #[arg(long)]
41 server: Option<String>,
42
43 /// Keep refreshing every second
44 #[arg(short = 'f', long)]
45 follow: bool,
46
47 /// Show image column
48 #[arg(long)]
49 image: bool,
50
51 /// Show ports column
52 #[arg(long)]
53 ports: bool,
54
55 /// Show size column
56 #[arg(long)]
57 size: bool,
58 },
59
60 /// Tail logs from an app
61 Logs {
62 /// App name
63 app: String,
64
65 /// Follow log output
66 #[arg(short, long)]
67 follow: bool,
68
69 /// Server to tail logs from (defaults to first server)
70 #[arg(long)]
71 server: Option<String>,
72 },
73
74 /// Stop an app's containers (keeps config, files, and DNS intact)
75 Stop {
76 /// App name to stop
77 app: String,
78
79 /// Stop only on this server (defaults to all assigned servers)
80 #[arg(long)]
81 server: Option<String>,
82 },
83
84 /// Restart an app's containers without redeploying
85 Restart {
86 /// App name to restart
87 app: String,
88
89 /// Restart only on this server (defaults to all assigned servers)
90 #[arg(long)]
91 server: Option<String>,
92 },
93
94 /// Remove an app: stop containers, clean up files, DNS, and fleet.toml
95 Remove {
96 /// App name to remove
97 app: String,
98
99 /// Skip confirmation prompt
100 #[arg(long)]
101 yes: bool,
102 },
103
104 /// Initialize a new fleet.toml in the current directory
105 Init,
106
107 /// Manage servers
108 Server {
109 #[command(subcommand)]
110 command: ServerCommand,
111 },
112
113 /// Manage apps in fleet.toml
114 App {
115 #[command(subcommand)]
116 command: AppCommand,
117 },
118
119 /// Manage GitHub Actions self-hosted runners
120 Runner {
121 #[command(subcommand)]
122 command: RunnerCommand,
123 },
124
125 /// Login to external services (runs all if no subcommand given)
126 Login {
127 #[command(subcommand)]
128 command: Option<LoginCommand>,
129 },
130
131 /// Manage app databases
132 Db {
133 #[command(subcommand)]
134 command: DbCommand,
135 },
136
137 /// Manage environment variables in fleet.env.toml
138 Env {
139 /// [app] [key=value...] — list/set env vars
140 args: Vec<String>,
141 },
142
143 /// Update flow CLI to the latest version
144 Update,
145}
146
147#[derive(Subcommand)]
148pub enum LoginCommand {
149 /// Set Cloudflare API token
150 Cf,
151
152 /// Set GitHub Container Registry token
153 Gh,
154}
155
156#[derive(Subcommand)]
157pub enum ServerCommand {
158 /// Add a new server and run Ansible to bootstrap it (interactive wizard if no args given)
159 Add {
160 /// Server name (used as identifier in fleet.toml)
161 name: Option<String>,
162
163 /// Server IP address
164 #[arg(long)]
165 ip: Option<String>,
166
167 /// Override hostname (default: {name}.{domain})
168 #[arg(long)]
169 host: Option<String>,
170
171 /// Deploy user (created by Ansible, used for future SSH)
172 #[arg(long)]
173 user: Option<String>,
174
175 /// SSH user for initial Ansible connection
176 #[arg(long)]
177 ssh_user: Option<String>,
178
179 /// Path to SSH public key for the deploy user
180 #[arg(long)]
181 ssh_key: Option<String>,
182 },
183
184 /// Remove a server from fleet.toml
185 Remove {
186 /// Server name to remove
187 name: String,
188 },
189}
190
191#[derive(Subcommand)]
192pub enum DbCommand {
193 /// Open an interactive psql shell
194 Shell {
195 /// App name (defaults to first app with postgres)
196 app: Option<String>,
197
198 /// Server to connect to (defaults to first server)
199 #[arg(long)]
200 server: Option<String>,
201 },
202
203 /// Dump the database to a local file
204 Dump {
205 /// App name (defaults to first app with postgres)
206 app: Option<String>,
207
208 /// Output file path (default: {app}.sql.gz)
209 #[arg(short, long)]
210 output: Option<String>,
211
212 /// Server to dump from (defaults to first server)
213 #[arg(long)]
214 server: Option<String>,
215 },
216
217 /// Restore the database from a local SQL file
218 Restore {
219 /// App name (defaults to first app with postgres)
220 app: Option<String>,
221
222 /// Path to .sql or .sql.gz file
223 file: String,
224
225 /// Skip confirmation prompt
226 #[arg(long)]
227 yes: bool,
228
229 /// Server to restore to (defaults to first server)
230 #[arg(long)]
231 server: Option<String>,
232 },
233
234 /// List available backups on the server
235 List {
236 /// App name (defaults to first app with postgres)
237 app: Option<String>,
238
239 /// Server to list backups from (defaults to first server)
240 #[arg(long)]
241 server: Option<String>,
242 },
243}
244
245#[derive(Subcommand)]
246pub enum RunnerCommand {
247 /// Add a self-hosted runner (interactive wizard if no args given)
248 Add {
249 /// Runner name (used as identifier in fleet.toml)
250 name: Option<String>,
251
252 /// Server to deploy to (must exist in fleet.toml)
253 #[arg(long)]
254 server: Option<String>,
255
256 /// Runner scope: org or repo
257 #[arg(long)]
258 scope: Option<String>,
259
260 /// Target org name or owner/repo
261 #[arg(long)]
262 target: Option<String>,
263
264 /// Runner label(s) (repeatable)
265 #[arg(long)]
266 label: Vec<String>,
267
268 /// Single-job ephemeral mode (default: true)
269 #[arg(long)]
270 ephemeral: bool,
271 },
272
273 /// Remove a runner from fleet.toml and clean up on server
274 Remove {
275 /// Runner name to remove
276 name: String,
277
278 /// Skip confirmation prompt
279 #[arg(long)]
280 yes: bool,
281 },
282
283 /// List runners and their status from GitHub API
284 List,
285}
286
287#[derive(Subcommand)]
288pub enum AppCommand {
289 /// Add a new app to fleet.toml (interactive wizard if no args given)
290 Add {
291 /// App name (used as identifier in fleet.toml)
292 name: Option<String>,
293
294 /// Docker image (e.g., ghcr.io/org/app:latest)
295 #[arg(long)]
296 image: Option<String>,
297
298 /// Server(s) to deploy to (must exist in fleet.toml, repeatable)
299 #[arg(long)]
300 server: Vec<String>,
301
302 /// Container port (required if routing is used)
303 #[arg(long)]
304 port: Option<u16>,
305
306 /// Domain hostname(s) for Caddy reverse proxy (repeatable)
307 #[arg(long)]
308 domain: Vec<String>,
309
310 /// Health check path (e.g., /health)
311 #[arg(long)]
312 health_path: Option<String>,
313
314 /// Health check interval (e.g., 5s, 1m)
315 #[arg(long)]
316 health_interval: Option<String>,
317
318 /// Direct port mapping(s) in external:internal[/protocol] format (repeatable)
319 #[arg(long, value_name = "EXTERNAL:INTERNAL[/PROTOCOL]")]
320 port_map: Vec<String>,
321
322 /// Deploy strategy: rolling (default) or recreate
323 #[arg(long)]
324 deploy_strategy: Option<String>,
325 },
326
327 /// Add a sidecar service to an existing app
328 AddService {
329 /// App name (must exist in fleet.toml)
330 app: String,
331
332 /// Service name
333 name: String,
334
335 /// Docker image for the service
336 #[arg(long)]
337 image: String,
338
339 /// Volume mount(s) in name:path format (repeatable)
340 #[arg(long)]
341 volume: Vec<String>,
342
343 /// Healthcheck command
344 #[arg(long)]
345 healthcheck: Option<String>,
346
347 /// Service this depends on (must exist in same app)
348 #[arg(long)]
349 depends_on: Option<String>,
350 },
351
352 /// Remove a sidecar service from an app
353 RemoveService {
354 /// App name
355 app: String,
356
357 /// Service name to remove
358 name: String,
359 },
360}