Skip to main content

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}