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