Skip to main content

envvault/cli/
gitignore.rs

1//! Shared `.gitignore` patching logic.
2//!
3//! Used by `init` (to add the vault directory) and `auth keyfile-generate`
4//! (to add the keyfile path).
5
6use std::fs;
7use std::path::Path;
8
9use crate::cli::output;
10
11/// Append `entry` to `.gitignore` if not already present.
12///
13/// Creates the file if it doesn't exist. Silently ignores write errors
14/// (non-fatal — gitignore is a convenience, not a requirement).
15pub fn patch_gitignore(project_dir: &Path, entry: &str) {
16    let gitignore_path = project_dir.join(".gitignore");
17
18    let existing = fs::read_to_string(&gitignore_path).unwrap_or_default();
19
20    if existing.lines().any(|line| line.trim() == entry) {
21        return;
22    }
23
24    let separator = if existing.ends_with('\n') || existing.is_empty() {
25        ""
26    } else {
27        "\n"
28    };
29
30    if fs::write(&gitignore_path, format!("{existing}{separator}{entry}\n")).is_ok() {
31        output::info(&format!("Added '{entry}' to .gitignore"));
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38    use tempfile::TempDir;
39
40    #[test]
41    fn adds_entry_to_new_gitignore() {
42        let dir = TempDir::new().unwrap();
43        patch_gitignore(dir.path(), ".envvault/");
44
45        let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
46        assert!(content.contains(".envvault/"));
47    }
48
49    #[test]
50    fn does_not_duplicate_entry() {
51        let dir = TempDir::new().unwrap();
52        fs::write(dir.path().join(".gitignore"), ".envvault/\n").unwrap();
53
54        patch_gitignore(dir.path(), ".envvault/");
55
56        let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
57        assert_eq!(content.matches(".envvault/").count(), 1);
58    }
59
60    #[test]
61    fn appends_with_newline_separator() {
62        let dir = TempDir::new().unwrap();
63        fs::write(dir.path().join(".gitignore"), "node_modules/").unwrap(); // no trailing newline
64
65        patch_gitignore(dir.path(), ".envvault/");
66
67        let content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
68        assert_eq!(content, "node_modules/\n.envvault/\n");
69    }
70}