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
22 /// Verify fleet.toml matches reality on servers
23 Check {
24 /// Filter by server name
25 #[arg(long)]
26 server: Option<String>,
27 },
28
29 /// Show fleet-wide status and container info
30 Status {
31 /// Filter by server name
32 #[arg(long)]
33 server: Option<String>,
34 },
35
36 /// Tail logs from an app
37 Logs {
38 /// App name
39 app: String,
40
41 /// Follow log output
42 #[arg(short, long)]
43 follow: bool,
44
45 /// Server to tail logs from (defaults to first server)
46 #[arg(long)]
47 server: Option<String>,
48 },
49
50 /// Stop an app's containers (keeps config, files, and DNS intact)
51 Stop {
52 /// App name to stop
53 app: String,
54
55 /// Stop only on this server (defaults to all assigned servers)
56 #[arg(long)]
57 server: Option<String>,
58 },
59
60 /// Restart an app's containers without redeploying
61 Restart {
62 /// App name to restart
63 app: String,
64
65 /// Restart only on this server (defaults to all assigned servers)
66 #[arg(long)]
67 server: Option<String>,
68 },
69
70 /// Remove an app: stop containers, clean up files, DNS, and fleet.toml
71 Remove {
72 /// App name to remove
73 app: String,
74
75 /// Skip confirmation prompt
76 #[arg(long)]
77 yes: bool,
78 },
79
80 /// Initialize a new fleet.toml in the current directory
81 Init,
82
83 /// Manage servers
84 Server {
85 #[command(subcommand)]
86 command: ServerCommand,
87 },
88
89 /// Manage apps in fleet.toml
90 App {
91 #[command(subcommand)]
92 command: AppCommand,
93 },
94
95 /// Login to external services (runs all if no subcommand given)
96 Login {
97 #[command(subcommand)]
98 command: Option<LoginCommand>,
99 },
100}
101
102#[derive(Subcommand)]
103pub enum LoginCommand {
104 /// Set Cloudflare API token
105 Cf,
106
107 /// Set GitHub Container Registry token
108 Gh,
109}
110
111#[derive(Subcommand)]
112pub enum ServerCommand {
113 /// Add a new server and run Ansible to bootstrap it
114 Add {
115 /// Server name (used as identifier in fleet.toml)
116 name: String,
117
118 /// Server IP address
119 #[arg(long)]
120 ip: String,
121
122 /// Override hostname (default: {name}.{domain})
123 #[arg(long)]
124 host: Option<String>,
125
126 /// Deploy user (created by Ansible, used for future SSH)
127 #[arg(long, default_value = "deploy")]
128 user: String,
129
130 /// SSH user for initial Ansible connection
131 #[arg(long, default_value = "root")]
132 ssh_user: String,
133
134 /// Path to SSH public key for the deploy user
135 #[arg(long)]
136 ssh_key: Option<String>,
137 },
138
139 /// Remove a server from fleet.toml
140 Remove {
141 /// Server name to remove
142 name: String,
143 },
144
145 /// Verify a server is properly set up
146 Check {
147 /// Server name (checks all if omitted)
148 name: Option<String>,
149 },
150}
151
152#[derive(Subcommand)]
153pub enum AppCommand {
154 /// Add a new app to fleet.toml
155 Add {
156 /// App name (used as identifier in fleet.toml)
157 name: String,
158
159 /// Docker image (e.g., ghcr.io/org/app:latest)
160 #[arg(long)]
161 image: String,
162
163 /// Server(s) to deploy to (must exist in fleet.toml, repeatable)
164 #[arg(long, required = true, num_args = 1..)]
165 server: Vec<String>,
166
167 /// Container port (required if routing is used)
168 #[arg(long)]
169 port: Option<u16>,
170
171 /// Route hostname(s) for Caddy reverse proxy (repeatable)
172 #[arg(long)]
173 route: Vec<String>,
174
175 /// Health check path (e.g., /health)
176 #[arg(long)]
177 health_path: Option<String>,
178
179 /// Health check interval (e.g., 5s, 1m)
180 #[arg(long)]
181 health_interval: Option<String>,
182
183 /// Direct port mapping(s) in external:internal[/protocol] format (repeatable)
184 #[arg(long, value_name = "EXTERNAL:INTERNAL[/PROTOCOL]")]
185 port_map: Vec<String>,
186
187 /// Deploy strategy: rolling (default) or recreate
188 #[arg(long, default_value = "rolling")]
189 deploy_strategy: String,
190 },
191
192 /// Add a sidecar service to an existing app
193 AddService {
194 /// App name (must exist in fleet.toml)
195 app: String,
196
197 /// Service name
198 name: String,
199
200 /// Docker image for the service
201 #[arg(long)]
202 image: String,
203
204 /// Volume mount(s) in name:path format (repeatable)
205 #[arg(long)]
206 volume: Vec<String>,
207
208 /// Healthcheck command
209 #[arg(long)]
210 healthcheck: Option<String>,
211
212 /// Service this depends on (must exist in same app)
213 #[arg(long)]
214 depends_on: Option<String>,
215 },
216
217 /// Remove a sidecar service from an app
218 RemoveService {
219 /// App name
220 app: String,
221
222 /// Service name to remove
223 name: String,
224 },
225}