use doot_core::Config; use std::path::{Path, PathBuf}; #[tracing::instrument(skip_all)] pub fn run(path: Option) -> anyhow::Result<()> { let source_dir = path.unwrap_or_else(Config::default_source_dir); let config = Config::new(source_dir.clone()); let is_default = source_dir == Config::default_config_dir(); tracing::debug!( config_dir = %config.config_dir.display(), state_dir = %config.state_dir.display(), "initializing doot" ); if !is_default { tracing::debug!(source_dir = %source_dir.display(), "custom source directory"); } config.ensure_dirs()?; if !is_default { std::fs::create_dir_all(&source_dir)?; } let config_file = config.config_dir.join("doot.doot"); if !config_file.exists() { let content = if is_default { EXAMPLE_CONFIG.to_string() } else { example_config_with_source(&source_dir) }; std::fs::write(&config_file, content)?; println!("created {}", config_file.display()); } let dotfiles_config_dir = source_dir.join("config"); std::fs::create_dir_all(&dotfiles_config_dir)?; if !is_default { let gitignore_path = source_dir.join(".gitignore"); if !gitignore_path.exists() { std::fs::write(&gitignore_path, GITIGNORE_CONTENT)?; println!("created {}", gitignore_path.display()); } } println!("doot initialized"); println!(); println!("structure:"); println!(" {}/doot.doot", config.config_dir.display()); println!(" {}/config/", config.config_dir.display()); println!(" state: {}", config.state_dir.display()); println!(); println!("next steps:"); println!( " 1. add dotfiles to {}/config/", config.config_dir.display() ); println!(" 2. edit {}/doot.doot", config.config_dir.display()); println!(" 3. run 'doot apply -n' to preview"); println!(" 4. run 'doot apply' to deploy"); Ok(()) } #[tracing::instrument(skip_all)] fn example_config_with_source(source_dir: &Path) -> String { format!( r#"# doot.doot # source directory: {source_dir} source_dir = "{source_dir}" {EXAMPLE_CONFIG}"#, source_dir = source_dir.display(), EXAMPLE_CONFIG = EXAMPLE_CONFIG_BODY ) } const EXAMPLE_CONFIG: &str = r#"# doot.doot # Dotfiles dotfile: source = "config/nvim" target = config_path("nvim") dotfile: source = "config/kitty" target = config_path("kitty") # Platform-specific if os == Os::MacOS: dotfile: source = "config/aerospace" target = "~/.config/aerospace" if os == Os::Linux: dotfile: source = "config/i3" target = "~/.config/i3" # Packages package: "ripgrep" package: "fd" package: "bat" package: "fzf" # Package with platform variants package: default = "fd" apt = "fd-find" "#; const EXAMPLE_CONFIG_BODY: &str = r#"# Dotfiles dotfile: source = "config/nvim" target = config_path("nvim") dotfile: source = "config/kitty" target = config_path("kitty") # Platform-specific if os == Os::MacOS: dotfile: source = "config/aerospace" target = "~/.config/aerospace" if os == Os::Linux: dotfile: source = "config/i3" target = "~/.config/i3" # Packages package: "ripgrep" package: "fd" package: "bat" package: "fzf" # Package with platform variants package: default = "fd" apt = "fd-find" "#; const GITIGNORE_CONTENT: &str = r#"# secrets (encrypted files are ok) secrets/*.key *.age.key # OS files .DS_Store Thumbs.db "#;