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