dotenv_space/cli.rs
1use clap::{Parser, Subcommand};
2
3#[derive(Parser)]
4#[command(
5 name = "evnx",
6 about = "Manage .env files — validation, secret scanning, and format conversion",
7 version,
8 author
9)]
10pub struct Cli {
11 #[command(subcommand)]
12 pub command: Commands,
13
14 #[arg(short, long, global = true)]
15 pub verbose: bool,
16
17 #[arg(short, long, global = true)]
18 pub quiet: bool,
19
20 #[arg(long, global = true)]
21 pub no_color: bool,
22}
23
24#[derive(Subcommand)]
25pub enum Commands {
26 /// Interactive project setup — generates .env.example
27 Init {
28 /// Project stack (python, nodejs, rust, go, php)
29 #[arg(long)]
30 stack: Option<String>,
31
32 /// Services to include (comma-separated)
33 #[arg(long)]
34 services: Option<String>,
35
36 /// Output path for .env.example
37 #[arg(long, default_value = ".")]
38 path: String,
39
40 /// Skip all prompts and use defaults
41 #[arg(short, long)]
42 yes: bool,
43 },
44
45 /// Check .env against .env.example, find issues
46 Validate {
47 /// Path to .env file
48 #[arg(long, default_value = ".env")]
49 env: String,
50
51 /// Path to .env.example file
52 #[arg(long, default_value = ".env.example")]
53 example: String,
54
55 /// Fail on warnings, not just errors
56 #[arg(long)]
57 strict: bool,
58
59 /// Auto-fix safe issues
60 #[arg(long)]
61 fix: bool,
62
63 /// Output format (pretty, json, github-actions)
64 #[arg(long, default_value = "pretty")]
65 format: String,
66
67 /// Always exit with 0 (useful in CI)
68 #[arg(long)]
69 exit_zero: bool,
70 },
71
72 /// Detect secrets that look real (AWS keys, tokens, etc.)
73 Scan {
74 /// Files/directories to scan
75 #[arg(default_value = ".")]
76 path: Vec<String>,
77
78 /// Exclude files matching pattern
79 #[arg(long, default_value = ".env.example")]
80 exclude: Vec<String>,
81
82 /// Only scan for specific patterns (aws, stripe, github, all)
83 #[arg(long)]
84 pattern: Vec<String>,
85
86 /// Skip obvious placeholders
87 #[arg(long)]
88 ignore_placeholders: bool,
89
90 /// Output format (pretty, json, sarif)
91 #[arg(long, default_value = "pretty")]
92 format: String,
93
94 /// Don't fail CI even if secrets found
95 #[arg(long)]
96 exit_zero: bool,
97 },
98
99 /// Compare .env vs .env.example — show missing/extra vars
100 Diff {
101 /// Path to .env file
102 #[arg(long, default_value = ".env")]
103 env: String,
104
105 /// Path to .env.example file
106 #[arg(long, default_value = ".env.example")]
107 example: String,
108
109 /// Show actual values (default: hide for security)
110 #[arg(long)]
111 show_values: bool,
112
113 /// Output format (pretty, json, patch)
114 #[arg(long, default_value = "pretty")]
115 format: String,
116
117 /// Reverse comparison (.env.example vs .env)
118 #[arg(long)]
119 reverse: bool,
120 },
121
122 /// Transform to different formats (JSON, YAML, shell, etc.)
123 Convert {
124 /// Input .env file
125 #[arg(long, default_value = ".env")]
126 env: String,
127
128 /// Target format (required in non-interactive mode)
129 #[arg(long)]
130 to: Option<String>,
131
132 /// Output file (default: stdout)
133 #[arg(long)]
134 output: Option<String>,
135
136 /// Include only matching vars (glob pattern)
137 #[arg(long)]
138 include: Option<String>,
139
140 /// Exclude matching vars
141 #[arg(long)]
142 exclude: Option<String>,
143
144 /// Base64-encode all values
145 #[arg(long)]
146 base64: bool,
147
148 /// Add prefix to all keys
149 #[arg(long)]
150 prefix: Option<String>,
151
152 /// Key transform (uppercase, lowercase, camelCase, snake_case)
153 #[arg(long)]
154 transform: Option<String>,
155 },
156
157 /// Full migration workflow to secret managers
158 #[cfg(feature = "migrate")]
159 Migrate {
160 /// Source type (env-file, aws, gcp, github, doppler)
161 #[arg(long)]
162 from: Option<String>,
163
164 /// Destination type
165 #[arg(long)]
166 to: Option<String>,
167
168 /// Source .env file
169 #[arg(long, default_value = ".env")]
170 source_file: String,
171
172 /// GitHub repository (OWNER/REPO)
173 #[arg(long)]
174 repo: Option<String>,
175
176 /// Secret name for AWS/GCP single-secret storage
177 #[arg(long)]
178 secret_name: Option<String>,
179
180 /// Show what would happen without making changes
181 #[arg(long)]
182 dry_run: bool,
183
184 /// Skip variables that already exist
185 #[arg(long)]
186 skip_existing: bool,
187
188 /// Overwrite existing secrets
189 #[arg(long)]
190 overwrite: bool,
191
192 /// GitHub Personal Access Token
193 #[arg(long, env = "GITHUB_TOKEN")]
194 github_token: Option<String>,
195
196 /// AWS profile to use
197 #[arg(long)]
198 aws_profile: Option<String>,
199 },
200
201 /// Keep .env and .env.example in sync
202 Sync {
203 /// Direction (forward: .env → .env.example, reverse: .env.example → .env)
204 #[arg(long, default_value = "forward")]
205 direction: String,
206
207 /// Add with placeholder values
208 #[arg(long)]
209 placeholder: bool,
210 },
211
212 /// Generate config files from templates
213 Template {
214 /// Input template file
215 #[arg(long)]
216 input: String,
217
218 /// Output file
219 #[arg(long)]
220 output: String,
221
222 /// .env file to use for values
223 #[arg(long, default_value = ".env")]
224 env: String,
225 },
226
227 /// Create encrypted backup of .env
228 #[cfg(feature = "backup")]
229 Backup {
230 /// .env file to backup
231 #[arg(default_value = ".env")]
232 env: String,
233
234 /// Output file
235 #[arg(long)]
236 output: Option<String>,
237 },
238
239 /// Restore from encrypted backup
240 #[cfg(feature = "backup")]
241 Restore {
242 /// Backup file
243 backup: String,
244
245 /// Output file
246 #[arg(long, default_value = ".env")]
247 output: String,
248 },
249
250 /// Diagnose common setup issues
251 Doctor {
252 /// Project directory to diagnose
253 #[arg(default_value = ".")]
254 path: String,
255 },
256
257 /// Generate shell completions
258 Completions {
259 /// Shell type (bash, zsh, fish, powershell)
260 shell: String,
261 },
262}