Skip to main content

greentic_setup/
cli_args.rs

1//! CLI argument definitions for greentic-setup.
2
3use std::path::PathBuf;
4
5use clap::{Args, Parser, Subcommand};
6
7#[derive(Parser, Debug)]
8#[command(name = "greentic-setup")]
9#[command(version)]
10#[command(about = "Greentic bundle setup CLI")]
11#[command(after_help = r#"EXAMPLES:
12  Interactive wizard:
13    greentic-setup ./my-bundle
14
15  Preview without executing:
16    greentic-setup --dry-run ./my-bundle
17
18  Generate answers template:
19    greentic-setup --dry-run --emit-answers answers.json ./my-bundle
20
21  Apply answers file:
22    greentic-setup --answers answers.json ./my-bundle.gtbundle
23
24  Advanced (bundle subcommands):
25    greentic-setup bundle init ./my-bundle
26    greentic-setup bundle add pack.gtpack --bundle ./my-bundle
27    greentic-setup bundle status --bundle ./my-bundle
28"#)]
29pub struct Cli {
30    /// Bundle path (.gtbundle file or directory)
31    #[arg(value_name = "BUNDLE")]
32    pub bundle: Option<PathBuf>,
33
34    /// Dry run - show wizard but don't execute
35    #[arg(long = "dry-run", global = true)]
36    pub dry_run: bool,
37
38    /// Emit answers template to file (combine with --dry-run to only generate)
39    #[arg(long = "emit-answers", value_name = "FILE", global = true)]
40    pub emit_answers: Option<PathBuf>,
41
42    /// Apply answers from file
43    #[arg(long = "answers", short = 'a', value_name = "FILE", global = true)]
44    pub answers: Option<PathBuf>,
45
46    /// Encryption/decryption key for answer documents that include secrets
47    #[arg(long = "key", value_name = "KEY", global = true)]
48    pub key: Option<String>,
49
50    /// Tenant identifier
51    #[arg(long = "tenant", short = 't', default_value = "demo", global = true)]
52    pub tenant: String,
53
54    /// Team identifier
55    #[arg(long = "team", global = true)]
56    pub team: Option<String>,
57
58    /// Environment (defaults to `local`; legacy `dev` remapped via the A4b
59    /// compat alias with a once-per-process warning until removal).
60    #[arg(long = "env", short = 'e', default_value = "local", global = true)]
61    pub env: String,
62
63    /// UI locale (BCP-47 tag, e.g., en, ja, id)
64    #[arg(long = "locale", global = true)]
65    pub locale: Option<String>,
66
67    /// Advanced mode — show all questions including optional ones
68    #[arg(long = "advanced", global = true)]
69    pub advanced: bool,
70
71    /// Launch web-based setup UI in browser (enabled by default).
72    /// Use --no-ui to disable the UI; stdin prompts may still be used.
73    #[arg(long = "ui", global = true, default_value_t = true)]
74    pub ui: bool,
75
76    /// Disable web UI; stdin prompts may still be used.
77    #[arg(long = "no-ui", global = true)]
78    pub no_ui: bool,
79
80    /// Strict non-interactive mode: no prompts, fail if answers incomplete
81    #[arg(long = "non-interactive", global = true)]
82    pub non_interactive: bool,
83
84    #[command(subcommand)]
85    pub command: Option<Command>,
86}
87
88#[derive(Subcommand, Debug)]
89pub enum Command {
90    /// Diagnose bundle setup inputs and generated setup outputs
91    Doctor(DoctorArgs),
92    /// Bundle lifecycle management (advanced)
93    #[command(subcommand)]
94    Bundle(Box<BundleCommand>),
95}
96
97#[derive(Args, Debug, Clone)]
98pub struct DoctorArgs {
99    /// Bundle path (.gtbundle file or directory)
100    #[arg(value_name = "BUNDLE")]
101    pub bundle: PathBuf,
102    /// Emit stable machine-readable JSON
103    #[arg(long = "json")]
104    pub json: bool,
105    /// Treat warnings as command failures
106    #[arg(long = "strict")]
107    pub strict: bool,
108    /// Include fix hints in human-readable output
109    #[arg(long = "fix-hints")]
110    pub fix_hints: bool,
111    /// Show informational diagnostics in human-readable output
112    #[arg(long = "show-info")]
113    pub show_info: bool,
114    /// Limit checks to one stage
115    #[arg(long = "stage", value_enum)]
116    pub stage: Option<DoctorStageArg>,
117}
118
119#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
120pub enum DoctorStageArg {
121    Setup,
122    Cache,
123    Locks,
124    Answers,
125    Runtime,
126    Routes,
127}
128
129#[derive(Subcommand, Debug, Clone)]
130pub enum BundleCommand {
131    /// Initialize a new bundle directory
132    Init(BundleInitArgs),
133    /// Add a pack to a bundle
134    Add(BundleAddArgs),
135    /// Run setup flow for provider(s) in a bundle
136    Setup(BundleSetupArgs),
137    /// Update a provider's configuration in a bundle
138    Update(BundleSetupArgs),
139    /// Remove a provider from a bundle
140    Remove(BundleRemoveArgs),
141    /// Build a portable bundle (copy + resolve)
142    Build(BundleBuildArgs),
143    /// List packs or flows in a bundle
144    List(BundleListArgs),
145    /// Show bundle status
146    Status(BundleStatusArgs),
147}
148
149#[derive(Args, Debug, Clone)]
150pub struct BundleInitArgs {
151    /// Bundle directory (default: current directory)
152    #[arg(value_name = "PATH")]
153    pub path: Option<PathBuf>,
154    /// Bundle name
155    #[arg(long = "name", short = 'n')]
156    pub name: Option<String>,
157}
158
159#[derive(Args, Debug, Clone)]
160pub struct BundleAddArgs {
161    /// Pack reference (local path or OCI reference)
162    #[arg(value_name = "PACK_REF")]
163    pub pack_ref: String,
164    /// Bundle directory (default: current directory)
165    #[arg(long = "bundle", short = 'b')]
166    pub bundle: Option<PathBuf>,
167    /// Tenant identifier
168    #[arg(long = "tenant", short = 't', default_value = "demo")]
169    pub tenant: String,
170    /// Team identifier
171    #[arg(long = "team")]
172    pub team: Option<String>,
173    /// Environment (defaults to `local`; legacy `dev` remapped via the A4b
174    /// compat alias with a once-per-process warning until removal).
175    #[arg(long = "env", short = 'e', default_value = "local")]
176    pub env: String,
177    /// Dry run (don't actually add)
178    #[arg(long = "dry-run")]
179    pub dry_run: bool,
180}
181
182#[derive(Args, Debug, Clone)]
183pub struct BundleSetupArgs {
184    /// Provider ID to setup/update (optional, setup all if not specified)
185    #[arg(value_name = "PROVIDER_ID")]
186    pub provider_id: Option<String>,
187    /// Bundle directory (default: current directory)
188    #[arg(long = "bundle", short = 'b')]
189    pub bundle: Option<PathBuf>,
190    /// Answers file (JSON/YAML)
191    #[arg(long = "answers", short = 'a')]
192    pub answers: Option<PathBuf>,
193    /// Encryption/decryption key for answer documents that include secrets
194    #[arg(long = "key", value_name = "KEY")]
195    pub key: Option<String>,
196    /// Tenant identifier
197    #[arg(long = "tenant", short = 't', default_value = "demo")]
198    pub tenant: String,
199    /// Team identifier
200    #[arg(long = "team")]
201    pub team: Option<String>,
202    /// Environment (defaults to `local`; legacy `dev` remapped via the A4b
203    /// compat alias with a once-per-process warning until removal).
204    #[arg(long = "env", short = 'e', default_value = "local")]
205    pub env: String,
206    /// Filter by domain (messaging/events/secrets/oauth/all)
207    #[arg(long = "domain", short = 'd', default_value = "all")]
208    pub domain: String,
209    /// Number of parallel setup operations
210    #[arg(long = "parallel", default_value = "1")]
211    pub parallel: usize,
212    /// Backup existing config before setup
213    #[arg(long = "backup")]
214    pub backup: bool,
215    /// Skip secrets initialization
216    #[arg(long = "skip-secrets-init")]
217    pub skip_secrets_init: bool,
218    /// Continue on error (best effort)
219    #[arg(long = "best-effort")]
220    pub best_effort: bool,
221    /// Populated from the global --non-interactive flag before dispatch.
222    #[arg(skip)]
223    pub non_interactive: bool,
224    /// Dry run (plan only, don't execute)
225    #[arg(long = "dry-run")]
226    pub dry_run: bool,
227    /// Emit answers template JSON (use with --dry-run)
228    #[arg(long = "emit-answers")]
229    pub emit_answers: Option<PathBuf>,
230    /// Advanced mode — show all questions including optional ones
231    #[arg(long = "advanced")]
232    pub advanced: bool,
233}
234
235#[derive(Args, Debug, Clone)]
236pub struct BundleRemoveArgs {
237    /// Provider ID to remove
238    #[arg(value_name = "PROVIDER_ID")]
239    pub provider_id: String,
240    /// Bundle directory (default: current directory)
241    #[arg(long = "bundle", short = 'b')]
242    pub bundle: Option<PathBuf>,
243    /// Tenant identifier
244    #[arg(long = "tenant", short = 't', default_value = "demo")]
245    pub tenant: String,
246    /// Team identifier
247    #[arg(long = "team")]
248    pub team: Option<String>,
249    /// Force removal without confirmation
250    #[arg(long = "force", short = 'f')]
251    pub force: bool,
252}
253
254#[derive(Args, Debug, Clone)]
255pub struct BundleBuildArgs {
256    /// Bundle directory (default: current directory)
257    #[arg(long = "bundle", short = 'b')]
258    pub bundle: Option<PathBuf>,
259    /// Output directory for portable bundle
260    #[arg(long = "out", short = 'o')]
261    pub out: PathBuf,
262    /// Tenant identifier
263    #[arg(long = "tenant", short = 't')]
264    pub tenant: Option<String>,
265    /// Team identifier
266    #[arg(long = "team")]
267    pub team: Option<String>,
268    /// Only include used providers
269    #[arg(long = "only-used-providers")]
270    pub only_used_providers: bool,
271    /// Run doctor validation after build
272    #[arg(long = "doctor")]
273    pub doctor: bool,
274    /// Skip doctor validation
275    #[arg(long = "skip-doctor")]
276    pub skip_doctor: bool,
277}
278
279#[derive(Args, Debug, Clone)]
280pub struct BundleListArgs {
281    /// Bundle directory (default: current directory)
282    #[arg(long = "bundle", short = 'b')]
283    pub bundle: Option<PathBuf>,
284    /// Filter by domain (messaging/events/secrets/oauth)
285    #[arg(long = "domain", short = 'd', default_value = "messaging")]
286    pub domain: String,
287    /// Show flows for a specific pack
288    #[arg(long = "pack", short = 'p')]
289    pub pack: Option<String>,
290    /// Output format (text/json)
291    #[arg(long = "format", default_value = "text")]
292    pub format: String,
293}
294
295#[derive(Args, Debug, Clone)]
296pub struct BundleStatusArgs {
297    /// Bundle directory (default: current directory)
298    #[arg(long = "bundle", short = 'b')]
299    pub bundle: Option<PathBuf>,
300    /// Output format (text/json)
301    #[arg(long = "format", default_value = "text")]
302    pub format: String,
303}