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
8mod common;
9mod docker;
10mod docker_compose;
11mod docker_database;
12mod postgres;
13mod profile;
14mod secrets;
15mod types;
16mod wizard;
17mod wizard_dry_run;
18mod wizard_prompts;
19
20use crate::shared::CommandResult;
21use anyhow::Result;
22use clap::Args;
23
24pub use types::*;
25
26#[derive(Debug, Args)]
27pub struct SetupArgs {
28    #[arg(
29        short,
30        long,
31        help = "Target environment name (e.g., dev, staging, prod)"
32    )]
33    pub environment: Option<String>,
34
35    #[arg(
36        long,
37        help = "Use Docker for PostgreSQL (default: use existing installation)"
38    )]
39    pub docker: bool,
40
41    #[arg(
42        long,
43        env = "SYSTEMPROMPT_DB_HOST",
44        default_value = "localhost",
45        help = "PostgreSQL host"
46    )]
47    pub db_host: String,
48
49    #[arg(
50        long,
51        env = "SYSTEMPROMPT_DB_PORT",
52        default_value = "5432",
53        help = "PostgreSQL port"
54    )]
55    pub db_port: u16,
56
57    #[arg(
58        long,
59        env = "SYSTEMPROMPT_DB_USER",
60        help = "PostgreSQL user (default: systemprompt_`<env>`)"
61    )]
62    pub db_user: Option<String>,
63
64    #[arg(
65        long,
66        env = "SYSTEMPROMPT_DB_PASSWORD",
67        help = "PostgreSQL password (auto-generated if not provided)"
68    )]
69    pub db_password: Option<String>,
70
71    #[arg(
72        long,
73        env = "SYSTEMPROMPT_DB_NAME",
74        help = "PostgreSQL database name (default: systemprompt_`<env>`)"
75    )]
76    pub db_name: Option<String>,
77
78    #[arg(long, env = "GEMINI_API_KEY", help = "Google AI (Gemini) API key")]
79    pub gemini_key: Option<String>,
80
81    #[arg(long, env = "ANTHROPIC_API_KEY", help = "Anthropic (Claude) API key")]
82    pub anthropic_key: Option<String>,
83
84    #[arg(long, env = "OPENAI_API_KEY", help = "OpenAI (GPT) API key")]
85    pub openai_key: Option<String>,
86
87    #[arg(long, env = "GITHUB_TOKEN", help = "GitHub token (optional)")]
88    pub github_token: Option<String>,
89
90    #[arg(long, help = "Run database migrations after setup")]
91    pub migrate: bool,
92
93    #[arg(
94        long,
95        conflicts_with = "migrate",
96        help = "Skip migrations (non-interactive default)"
97    )]
98    pub no_migrate: bool,
99
100    #[arg(long, help = "Preview setup without creating files or making changes")]
101    pub dry_run: bool,
102
103    #[arg(short = 'y', long, help = "Skip confirmation prompts")]
104    pub yes: bool,
105}
106
107impl SetupArgs {
108    pub fn effective_db_user(&self, env_name: &str) -> String {
109        self.db_user
110            .clone()
111            .unwrap_or_else(|| format!("systemprompt_{}", env_name))
112    }
113
114    pub fn effective_db_name(&self, env_name: &str) -> String {
115        self.db_name
116            .clone()
117            .unwrap_or_else(|| format!("systemprompt_{}", env_name))
118    }
119
120    pub const fn has_ai_provider(&self) -> bool {
121        self.gemini_key.is_some() || self.anthropic_key.is_some() || self.openai_key.is_some()
122    }
123}
124
125pub async fn execute(
126    args: SetupArgs,
127    config: &crate::CliConfig,
128) -> Result<CommandResult<SetupOutput>> {
129    wizard::execute(args, config).await
130}