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 (dev/staging/prod)
59    #[arg(long = "env", short = 'e', default_value = "dev", global = true)]
60    pub env: String,
61
62    /// UI locale (BCP-47 tag, e.g., en, ja, id)
63    #[arg(long = "locale", global = true)]
64    pub locale: Option<String>,
65
66    /// Advanced mode — show all questions including optional ones
67    #[arg(long = "advanced", global = true)]
68    pub advanced: bool,
69
70    /// Launch web-based setup UI in browser (enabled by default).
71    /// Use --no-ui to disable the UI; stdin prompts may still be used.
72    #[arg(long = "ui", global = true, default_value_t = true)]
73    pub ui: bool,
74
75    /// Disable web UI; stdin prompts may still be used.
76    #[arg(long = "no-ui", global = true)]
77    pub no_ui: bool,
78
79    /// Strict non-interactive mode: no prompts, fail if answers incomplete
80    #[arg(long = "non-interactive", global = true)]
81    pub non_interactive: bool,
82
83    /// Fixed port for the setup UI server (default: random)
84    #[arg(long = "port", value_name = "PORT", global = true)]
85    pub port: Option<u16>,
86
87    #[command(subcommand)]
88    pub command: Option<Command>,
89}
90
91#[derive(Subcommand, Debug)]
92pub enum Command {
93    /// Bundle lifecycle management (advanced)
94    #[command(subcommand)]
95    Bundle(BundleCommand),
96}
97
98#[derive(Subcommand, Debug, Clone)]
99pub enum BundleCommand {
100    /// Initialize a new bundle directory
101    Init(BundleInitArgs),
102    /// Add a pack to a bundle
103    Add(BundleAddArgs),
104    /// Run setup flow for provider(s) in a bundle
105    Setup(BundleSetupArgs),
106    /// Update a provider's configuration in a bundle
107    Update(BundleSetupArgs),
108    /// Remove a provider from a bundle
109    Remove(BundleRemoveArgs),
110    /// Build a portable bundle (copy + resolve)
111    Build(BundleBuildArgs),
112    /// List packs or flows in a bundle
113    List(BundleListArgs),
114    /// Show bundle status
115    Status(BundleStatusArgs),
116}
117
118#[derive(Args, Debug, Clone)]
119pub struct BundleInitArgs {
120    /// Bundle directory (default: current directory)
121    #[arg(value_name = "PATH")]
122    pub path: Option<PathBuf>,
123    /// Bundle name
124    #[arg(long = "name", short = 'n')]
125    pub name: Option<String>,
126}
127
128#[derive(Args, Debug, Clone)]
129pub struct BundleAddArgs {
130    /// Pack reference (local path or OCI reference)
131    #[arg(value_name = "PACK_REF")]
132    pub pack_ref: String,
133    /// Bundle directory (default: current directory)
134    #[arg(long = "bundle", short = 'b')]
135    pub bundle: Option<PathBuf>,
136    /// Tenant identifier
137    #[arg(long = "tenant", short = 't', default_value = "demo")]
138    pub tenant: String,
139    /// Team identifier
140    #[arg(long = "team")]
141    pub team: Option<String>,
142    /// Environment (dev/staging/prod)
143    #[arg(long = "env", short = 'e', default_value = "dev")]
144    pub env: String,
145    /// Dry run (don't actually add)
146    #[arg(long = "dry-run")]
147    pub dry_run: bool,
148}
149
150#[derive(Args, Debug, Clone)]
151pub struct BundleSetupArgs {
152    /// Provider ID to setup/update (optional, setup all if not specified)
153    #[arg(value_name = "PROVIDER_ID")]
154    pub provider_id: Option<String>,
155    /// Bundle directory (default: current directory)
156    #[arg(long = "bundle", short = 'b')]
157    pub bundle: Option<PathBuf>,
158    /// Answers file (JSON/YAML)
159    #[arg(long = "answers", short = 'a')]
160    pub answers: Option<PathBuf>,
161    /// Encryption/decryption key for answer documents that include secrets
162    #[arg(long = "key", value_name = "KEY")]
163    pub key: Option<String>,
164    /// Tenant identifier
165    #[arg(long = "tenant", short = 't', default_value = "demo")]
166    pub tenant: String,
167    /// Team identifier
168    #[arg(long = "team")]
169    pub team: Option<String>,
170    /// Environment (dev/staging/prod)
171    #[arg(long = "env", short = 'e', default_value = "dev")]
172    pub env: String,
173    /// Filter by domain (messaging/events/secrets/oauth/all)
174    #[arg(long = "domain", short = 'd', default_value = "all")]
175    pub domain: String,
176    /// Number of parallel setup operations
177    #[arg(long = "parallel", default_value = "1")]
178    pub parallel: usize,
179    /// Backup existing config before setup
180    #[arg(long = "backup")]
181    pub backup: bool,
182    /// Skip secrets initialization
183    #[arg(long = "skip-secrets-init")]
184    pub skip_secrets_init: bool,
185    /// Continue on error (best effort)
186    #[arg(long = "best-effort")]
187    pub best_effort: bool,
188    /// Populated from the global --non-interactive flag before dispatch.
189    #[arg(skip)]
190    pub non_interactive: bool,
191    /// Dry run (plan only, don't execute)
192    #[arg(long = "dry-run")]
193    pub dry_run: bool,
194    /// Emit answers template JSON (use with --dry-run)
195    #[arg(long = "emit-answers")]
196    pub emit_answers: Option<PathBuf>,
197    /// Advanced mode — show all questions including optional ones
198    #[arg(long = "advanced")]
199    pub advanced: bool,
200    /// Fixed port for the setup UI server (default: random)
201    #[arg(long = "port", value_name = "PORT")]
202    pub port: Option<u16>,
203}
204
205#[derive(Args, Debug, Clone)]
206pub struct BundleRemoveArgs {
207    /// Provider ID to remove
208    #[arg(value_name = "PROVIDER_ID")]
209    pub provider_id: String,
210    /// Bundle directory (default: current directory)
211    #[arg(long = "bundle", short = 'b')]
212    pub bundle: Option<PathBuf>,
213    /// Tenant identifier
214    #[arg(long = "tenant", short = 't', default_value = "demo")]
215    pub tenant: String,
216    /// Team identifier
217    #[arg(long = "team")]
218    pub team: Option<String>,
219    /// Force removal without confirmation
220    #[arg(long = "force", short = 'f')]
221    pub force: bool,
222}
223
224#[derive(Args, Debug, Clone)]
225pub struct BundleBuildArgs {
226    /// Bundle directory (default: current directory)
227    #[arg(long = "bundle", short = 'b')]
228    pub bundle: Option<PathBuf>,
229    /// Output directory for portable bundle
230    #[arg(long = "out", short = 'o')]
231    pub out: PathBuf,
232    /// Tenant identifier
233    #[arg(long = "tenant", short = 't')]
234    pub tenant: Option<String>,
235    /// Team identifier
236    #[arg(long = "team")]
237    pub team: Option<String>,
238    /// Only include used providers
239    #[arg(long = "only-used-providers")]
240    pub only_used_providers: bool,
241    /// Run doctor validation after build
242    #[arg(long = "doctor")]
243    pub doctor: bool,
244    /// Skip doctor validation
245    #[arg(long = "skip-doctor")]
246    pub skip_doctor: bool,
247}
248
249#[derive(Args, Debug, Clone)]
250pub struct BundleListArgs {
251    /// Bundle directory (default: current directory)
252    #[arg(long = "bundle", short = 'b')]
253    pub bundle: Option<PathBuf>,
254    /// Filter by domain (messaging/events/secrets/oauth)
255    #[arg(long = "domain", short = 'd', default_value = "messaging")]
256    pub domain: String,
257    /// Show flows for a specific pack
258    #[arg(long = "pack", short = 'p')]
259    pub pack: Option<String>,
260    /// Output format (text/json)
261    #[arg(long = "format", default_value = "text")]
262    pub format: String,
263}
264
265#[derive(Args, Debug, Clone)]
266pub struct BundleStatusArgs {
267    /// Bundle directory (default: current directory)
268    #[arg(long = "bundle", short = 'b')]
269    pub bundle: Option<PathBuf>,
270    /// Output format (text/json)
271    #[arg(long = "format", default_value = "text")]
272    pub format: String,
273}