doot/crates/doot-cli/src/commands/init.rs
2026-02-05 23:18:33 -06:00

152 lines
3.5 KiB
Rust

use doot_core::Config;
use std::path::{Path, PathBuf};
#[tracing::instrument(skip_all)]
pub fn run(path: Option<PathBuf>) -> 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
"#;