systemprompt_cli/commands/admin/setup/
mod.rs1pub mod ai_config;
9pub mod catalog;
10pub mod common;
11pub mod ddl;
12mod docker;
13pub mod docker_compose;
14mod docker_database;
15pub mod postgres;
16mod profile;
17mod profile_sections;
18mod secrets;
19mod types;
20mod wizard;
21mod wizard_dry_run;
22mod wizard_prompts;
23
24use crate::shared::CommandOutput;
25use anyhow::Result;
26use clap::Args;
27use systemprompt_models::none_if_blank;
28
29pub use secrets::SecretsData;
30pub use types::*;
31
32#[derive(Debug, Args)]
33#[expect(
34 clippy::struct_excessive_bools,
35 reason = "each bool is an independent clap CLI flag, not a state machine"
36)]
37pub struct SetupArgs {
38 #[arg(
39 short,
40 long,
41 help = "Target environment name (e.g., dev, staging, prod)"
42 )]
43 pub environment: Option<String>,
44
45 #[arg(
46 long,
47 help = "Use Docker for PostgreSQL (default: use existing installation)"
48 )]
49 pub docker: bool,
50
51 #[arg(
52 long,
53 env = "SYSTEMPROMPT_DB_HOST",
54 default_value = "localhost",
55 help = "PostgreSQL host"
56 )]
57 pub db_host: String,
58
59 #[arg(
60 long,
61 env = "SYSTEMPROMPT_DB_PORT",
62 default_value = "5432",
63 help = "PostgreSQL port"
64 )]
65 pub db_port: u16,
66
67 #[arg(
68 long,
69 env = "SYSTEMPROMPT_DB_USER",
70 help = "PostgreSQL user (default: systemprompt_`<env>`)"
71 )]
72 pub db_user: Option<String>,
73
74 #[arg(
75 long,
76 env = "SYSTEMPROMPT_DB_PASSWORD",
77 help = "PostgreSQL password (auto-generated if not provided)"
78 )]
79 pub db_password: Option<String>,
80
81 #[arg(
82 long,
83 env = "SYSTEMPROMPT_DB_NAME",
84 help = "PostgreSQL database name (default: systemprompt_`<env>`)"
85 )]
86 pub db_name: Option<String>,
87
88 #[arg(long, env = "GEMINI_API_KEY", help = "Google AI (Gemini) API key")]
89 pub gemini_key: Option<String>,
90
91 #[arg(long, env = "ANTHROPIC_API_KEY", help = "Anthropic (Claude) API key")]
92 pub anthropic_key: Option<String>,
93
94 #[arg(long, env = "OPENAI_API_KEY", help = "OpenAI (GPT) API key")]
95 pub openai_key: Option<String>,
96
97 #[arg(long, env = "GITHUB_TOKEN", help = "GitHub token (optional)")]
98 pub github_token: Option<String>,
99
100 #[arg(
101 long,
102 env = "SYSTEMPROMPT_DEFAULT_PROVIDER",
103 help = "Provider to make the default (gemini | anthropic | openai); must have a key. \
104 In interactive mode the selected provider is used instead."
105 )]
106 pub default_provider: Option<String>,
107
108 #[arg(long, help = "Run database migrations after setup")]
109 pub migrate: bool,
110
111 #[arg(
112 long,
113 conflicts_with = "migrate",
114 help = "Skip migrations (non-interactive default)"
115 )]
116 pub no_migrate: bool,
117
118 #[arg(long, help = "Preview setup without creating files or making changes")]
119 pub dry_run: bool,
120
121 #[arg(short = 'y', long, help = "Skip confirmation prompts")]
122 pub yes: bool,
123
124 #[arg(
125 long,
126 help = "Overwrite existing profile/secrets files (default: preserve them)"
127 )]
128 pub force: bool,
129}
130
131impl SetupArgs {
132 pub fn effective_db_user(&self, env_name: &str) -> String {
133 self.db_user
134 .clone()
135 .unwrap_or_else(|| format!("systemprompt_{}", env_name))
136 }
137
138 pub fn effective_db_name(&self, env_name: &str) -> String {
139 self.db_name
140 .clone()
141 .unwrap_or_else(|| format!("systemprompt_{}", env_name))
142 }
143
144 pub const fn has_ai_provider(&self) -> bool {
145 self.gemini_key.is_some() || self.anthropic_key.is_some() || self.openai_key.is_some()
146 }
147
148 #[must_use]
149 fn normalized(mut self) -> Self {
150 self.gemini_key = none_if_blank(self.gemini_key);
151 self.anthropic_key = none_if_blank(self.anthropic_key);
152 self.openai_key = none_if_blank(self.openai_key);
153 self.github_token = none_if_blank(self.github_token);
154 self.default_provider = none_if_blank(self.default_provider);
155 self
156 }
157}
158
159pub async fn execute(
160 args: SetupArgs,
161 ctx: &crate::context::CommandContext,
162) -> Result<CommandOutput> {
163 wizard::execute(args.normalized(), ctx.prompter(), &ctx.cli).await
164}