Skip to main content

systemprompt_cli/commands/admin/setup/
mod.rs

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